【问题标题】:New APNS Provider API and PHP新的 APNS 提供程序 API 和 PHP
【发布时间】:2016-01-08 18:53:57
【问题描述】:

我开始根据this 创建一些代码,用于从 PHP 发送推送通知。

但是,现在我了解到有一个新的 API 使用 HTTP/2 并在响应中提供反馈,我正试图弄清楚我需要做什么才能获得该反馈。

我找不到任何教程或示例代码来指导我(我猜是因为它太新了)。

是否可以使用stream_socket_client() 方法通过新的提供程序 API 连接到 APNS?我如何获得反馈?我现在从fwrite($fp, $msg, strlen($msg)) 得到的只是一个数字。出于所有意图和目的,您可以认为我的代码与the SO question I based my code on中的代码相同

谢谢!

【问题讨论】:

    标签: php ios iphone apple-push-notifications


    【解决方案1】:

    借助新的 HTTP/2 APNS 提供程序 API,您可以使用 curl 发送推送通知。

    编辑

    在继续之前(如@Madox 所述),应安装 openssl >= 1.0.2e(最好从包中)。用命令验证

    openssl version
    

    a) 您的 PHP 版本应该 >= 5.5.24,以便定义常量 CURL_HTTP_VERSION_2_0。

    b) 确保您的系统中安装了 curl 版本 7.46+

    curl --version
    

    c) Curl 应该启用 http/2 支持。在输入上一个命令时的输出中,您应该会看到如下一行:

    Features: IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets 
    

    如果 HTTP2 没有出现,你可以按照这个优秀的教程安装 http/2 for curl https://serversforhackers.com/video/curl-with-http2-support

    验证 curl 检测到 openssl >= 1.0.2e,执行 curl --version 应该输出如下内容:

    curl 7.47.1 (x86_64-pc-linux-gnu) libcurl/7.47.1 OpenSSL/1.0.2f zlib/1.2.8 libidn/1.28 nghttp2/1.8.0-DEV librtmp/2.3
    

    e) 一切都安装好后,您可以在命令行中对其进行测试:

    curl -d '{"aps":{"alert":"hi","sound":"default"}}' \ 
    --cert <your-certificate.pem>:<certificate-password> \ 
    -H "apns-topic: <your-app-bundle-id>" \ 
    --http2  \ 
    https://api.development.push.apple.com/3/device/<device-token>
    

    f) 这是我成功尝试过的 PHP 示例代码:

    if(defined('CURL_HTTP_VERSION_2_0')){
    
        $device_token   = '...';
        $pem_file       = 'path to your pem file';
        $pem_secret     = 'your pem secret';
        $apns_topic     = 'your apns topic. Can be your app bundle ID';
    
    
        $sample_alert = '{"aps":{"alert":"hi","sound":"default"}}';
        $url = "https://api.development.push.apple.com/3/device/$device_token";
    
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $sample_alert);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("apns-topic: $apns_topic"));
        curl_setopt($ch, CURLOPT_SSLCERT, $pem_file);
        curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pem_secret);
        $response = curl_exec($ch);
        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
        //On successful response you should get true in the response and a status code of 200
        //A list of responses and status codes is available at 
        //https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH107-SW1
    
        var_dump($response);
        var_dump($httpcode);
    
    }
    

    【讨论】:

    • 谢谢 :) ... PHP 是 5.6.17,curl 是 7.46.0 并显示 HTTP2 支持。当我尝试步骤 d 或 e 时,我收到响应“?@@?HTTP/2 客户端序言字符串丢失或损坏。接收字节的十六进制转储:[十六进制转储]”。有什么想法吗?
    • 我在让它工作时遇到了问题,我的问题在这里更多:stackoverflow.com/questions/35416247/…
    • @BenHolness 根据我的经验,这个错误与所使用的 openssl 版本有关。您应该使用版本 >= 1.0.2e 的 openssl 编译 curl。使用命令 curl --version 进行验证。
    • 感谢这个非常明确的答案。我可以从命令行成功发送推送通知,但是在使用您的 PHP 代码时,我不断收到此错误:HTTP/2 client preface string missing or corrupt. Hex dump for received bytes ...。知道可能是什么问题吗?
    • @Nickkk 我目前遇到了同样的问题。你有没有找到解决这个问题的方法?我刚刚检查过,我所有的包都足够新来处理这个问题,它在命令行上工作,但不在脚本中。我在脚本中转储了 openssl 的版本号,它似乎使用了我安装的旧版本(使用 1.0.1t 虽然安装了 1.1.0e)。刚才有个同事告诉我,PHP有自己的openssl包,有问题..
    【解决方案2】:

    我想在 tiempor3al 答案中添加一些信息。

    1) curl 必须使用 openssl 版本 >=1.0.2 编译才能完全支持 http/2。当我使用 CentOS 股票 openssl-1.0.1e 编译它时,我收到“?@@?HTTP/2 客户端前言字符串丢失或损坏...”错误。

    2) 如果你编译的 php 模块 mod_curl.so 版本没有 CURL_HTTP_VERSION_2_0 常量,你可以用整数 3 替换它:

    curl_setopt($ch, CURLOPT_HTTP_VERSION, 3);

    【讨论】:

    • 我在openssl 1.0.2k 上,我仍然收到string missing or corrupt
    【解决方案3】:

    我可以使用 php CURL 通过 HTTP2 成功发送推送,并直接在响应正文中阅读反馈(这里我写了一个关于如何做到这一点的简短教程:Sending Push Notification with HTTP2 (and PHP))。 我认为您可以检查从套接字读取的响应正文(我不记得确切的 php 函数,也许是“fgets”)。

    【讨论】:

      【解决方案4】:

      我使用的是 CentOS 6,解决的方法是从源 cURL 和 OpenSSL 安装。

      它可能看起来很简单,但我花了 3 天时间才弄清楚包的正确配置,所以我想我可以通过在这里发布我所做的来帮助某人。

      这对我来说有点棘手,因为我不习惯从源代码安装包,但我现在可以使用 HTTPS over HTTP/2 连接到 APN。

      我所做的细节在这里:

      1. 下载并解压 cURL 和 OpenSSL:

        wget https://curl.haxx.se/download/curl-7.47.1.tar.gz
        wget https://www.openssl.org/source/openssl-1.0.2h.tar.gz
        
      2. 使用以下标志配置 OpenSSL(我不确定它们的作用,但对我有用):

        export CXXFLAGS="$CXXFLAGS -fPIC"
        ./config zlib enable-ssl3 enable-shared
        
      3. 制作并安装 OpenSSL

      4. 使用以下标志配置 cURL:

        ./configure --with-ssl=/usr/local/ssl/
        
      5. 制作和安装 cURL

      6. 将 LD_LIBRARY_PATH 设置为 /usr/local/ssl/lib/

        export LD_LIBRARY_PATH=/usr/local/ssl/lib/                  
        
      7. 测试

        /usr/local/bin/curl -v -d '{"aps":{"alert":"hi","sound":"default"}}' --cert cert.crt --key cert.key -H "apns-topic: topics" --http2 https://api.development.push.apple.com:443/3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
        

      结果:

      *   Trying 17.172.238.203...
      * Connected to api.development.push.apple.com (17.172.238.203) port 443 (#0)
      * ALPN, offering h2
      * ALPN, offering http/1.1
      * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
      * successfully set certificate verify locations:
      *   CAfile: /etc/pki/tls/certs/ca-bundle.crt
        CApath: none
      * TLSv1.2 (OUT), TLS header, Certificate Status (22):
      * TLSv1.2 (OUT), TLS handshake, Client hello (1):
      * TLSv1.2 (IN), TLS handshake, Server hello (2):
      * TLSv1.2 (IN), TLS handshake, Certificate (11):
      * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
      * TLSv1.2 (IN), TLS handshake, Request CERT (13):
      * TLSv1.2 (IN), TLS handshake, Server finished (14):
      * TLSv1.2 (OUT), TLS handshake, Certificate (11):
      * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
      * TLSv1.2 (OUT), TLS handshake, CERT verify (15):
      * TLSv1.2 (OUT), TLS change cipher, Client hello (1):
      * TLSv1.2 (OUT), TLS handshake, Finished (20):
      * TLSv1.2 (IN), TLS change cipher, Client hello (1):
      * TLSv1.2 (IN), TLS handshake, Finished (20):
      * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
      * ALPN, server accepted to use h2
      * Server certificate:
      *  subject: CN=api.development.push.apple.com; OU=management:idms.group.533599; O=Apple Inc.; ST=California; C=US
      *  start date: Jun 19 01:49:43 2015 GMT
      *  expire date: Jul 18 01:49:43 2017 GMT
      *  subjectAltName: host "api.development.push.apple.com" matched cert's "api.development.push.apple.com"
      *  issuer: CN=Apple IST CA 2 - G1; OU=Certification Authority; O=Apple Inc.; C=US
      *  SSL certificate verify ok.
      * Using HTTP2, server supports multi-use
      * Connection state changed (HTTP/2 confirmed)
      * TCP_NODELAY set
      * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
      * Using Stream ID: 1 (easy handle 0x1091110)
      > POST /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0 HTTP/1.1
      > Host: api.development.push.apple.com
      > User-Agent: curl/7.48.0
      > Accept: */*
      > apns-topic: topics
      > Content-Length: 40
      > Content-Type: application/x-www-form-urlencoded
      >
      * Connection state changed (MAX_CONCURRENT_STREAMS updated)!
      * We are completely uploaded and fine
      < HTTP/2.0 400
      <
      * Connection #0 to host api.development.push.apple.com left intact
      {"reason":"BadDeviceToken"}
      

      如你所见,我摆脱了丑陋

      ▒@@▒HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 504f5354202f332f6465766963652f746573742048545450
      

      【讨论】:

      • 下一步是使用此配置构建 PHP CURL 模块
      【解决方案5】:

      按照本指南 [http://cloudfields.net/blog/ios-push-notifications-encryption/][1] 生成并合并您的证书和私钥。 一旦您按照指南中的说明合并您的 sslcert 和 pkey 并使用相同的文件名,只需尝试下面的 curl 命令。

      curl -X POST -H 'apns-topic: com.mycompany.ios.BadassApp' -d '{"aps":{"content-available":1,"alert":"hi","sound":"default"}}' --cert apns_cert.pem:yourCertPassword --http2 'https://api.development.push.apple.com:443/3/device/b8de1sf067effefc398d792205146fc67dn0e96b0ff21ds81cabe384bbe71353'
      

      【讨论】:

        【解决方案6】:

        为了解决 PHP 5.6 中的 HTTP/2 client preface string missing or corrupt 错误,我创建了一个 ApacheCLI PHP Docker 镜像,您可能想要依赖它,或者只是查看我在 Dockerfile 中所做的事情来构建您自己的镜像。虽然我没有尝试过,但可能同样适用于 PHP 7.0。

        【讨论】:

        • 您能否为其他不知道如何实现您的代码的凡人添加一些安装说明?谢谢!
        • 如果你使用 Docker,你只需要使用 norbertm/php-5.6-apache-http2:5.6.31 或 norbertm/php-5.6-cli-http2:5.6.31。如果不使用 Docker,您需要做的是使用 nghttp2 (here is how I did it) 构建 curl,然后使用该 curl (here is how the official PHP Docker image does that) 构建 PHP
        【解决方案7】:

        是的,有可能:

        $errno=0;$errstr="";
        $sock = stream_socket_client(
          "api.push.apple.com:443",
          $errno,
          $errstr,
          4,
          STREAM_CLIENT_CONNECT,
          stream_context_create(["ssl" => ["local_cert" => "/path/to/cert.pem","verify_peer"=>false,"verify_peer_name"=>false]])
        );
        stream_socket_enable_crypto($sock, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
        

        【讨论】:

        • 您好!你能分享所有发送通知的代码吗?您“构建二进制通知”,例如: $payload = json_encode($body); chr(0) 。包('n',32)。包('H*',$device_token)。包('n',strlen($payload))。 $有效载荷; ?
        猜你喜欢
        • 2016-11-19
        • 2012-07-27
        • 2011-08-30
        • 2018-06-25
        • 1970-01-01
        • 2021-06-05
        • 1970-01-01
        • 2011-03-28
        • 2016-06-18
        相关资源
        最近更新 更多