【问题标题】:PHP Apple Enhanced Push Notification read error responsePHP Apple 增强推送通知读取错误响应
【发布时间】:2012-04-20 23:22:05
【问题描述】:

在 PHP 中,如何使用 fread() 来检查发送增强推送通知时是否有错误响应?

我已经阅读了 Apple 文档、通过 Google 发布的一些含糊不清的帖子,以及关于 SO 的几个问题/答案,但这仍然非常令人困惑。

这是我看到的: http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html Reading error from Apple enhanced push notification with PHP iPhone Push Notification - Error response problem

我将在下面回答我自己的问题,基于以下事实:(1) 我发现这是一个非常令人困惑的话题,并且 (2) 我必须通过大量的试验和错误将信息拼凑在一起才能得到它工作,以及 (3) 这篇博客文章说鼓励它:https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

【问题讨论】:

    标签: php ios push-notification apple-push-notifications


    【解决方案1】:

    我不确定您的代码内容,但您应该尝试ApnsPHP 它经过充分测试,工作正常,能够为您处理所有可能的异常和错误。

    其他选择

    https://github.com/sebastianborggrewe/PHP-Apple-Push-Notification-Server https://github.com/bortuzar/PHP-Mysql---Apple-Push-Notification-Server

    已经测试了 3 i 个示例中的 2 个,并且没有实施和错误管理问题。

    谢谢

    :)

    【讨论】:

    • 感谢您提供这些替代方案。
    【解决方案2】:

    当你发送推送通知时,有几个问题:

    • 如果出现问题,Apple 会断开您的连接,但您并不知道。当您使用基本通知时,无法知道它们是否都已发送。 解决方案: 这是使用增强通知然后检查错误响应的全部要点。请注意,我们将在数据库查询中使用“ORDER BY id”,然后使用 id 作为我们在通知中发送的标识符。这样,如果出现问题,我们就可以确切地知道数据库中的哪一行导致了问题(因此我们知道 Apple 何时断开我们并停止发送通知)。然后,我们可以继续向导致问题的行之后的所有行发送推送通知,而无需重新发送到我们已经发送到的行。

    • 如果一切正常,Apple 不会发回任何响应,因此这可能会导致您的脚本暂停并永远等待,而 fread() 正在等待未到来的数据。 解决方案: 需要将 stream_set_blocking 设置为 0,以便 fread 总是立即返回。请注意,这会导致 fread 在收到错误响应之前可能返回另一个小问题,但请参阅代码中的解决方法,即在所有发送完成后暂停 1/2 秒,然后再检查一次 fread .

    • 您可以发送多个推送通知,其速度要比错误响应速度快得多。 解决方案:这与上面提到的解决方法相同...在所有发送完成后暂停 1/2 秒,然后再检查一次 fread。

    这是我使用 PHP 的解决方案,它解决了我遇到的所有问题。它非常基本,但可以完成工作。我已经测试了一次发送几个通知以及一次发送 120,000 个通知。

    <?php
    /*
     * Read Error Response when sending Apple Enhanced Push Notification
     *
     * This assumes your iOS devices have the proper code to add their device tokens
     * to the db and also the proper code to receive push notifications when sent.
     *
     */
    
    //database
    $host = "localhost";
    $user = "my_db_username";
    $pass = "my_db_password";
    $dbname = "my_db_name";
    $con = mysql_connect($host, $user, $pass);
    if (!$con) {
        die('Could not connect to database: ' . mysql_error());
    } else {
        mysql_select_db($dbname, $con);
    }
    
    // IMPORTANT: make sure you ORDER BY id column
    $result = mysql_query("SELECT id,token FROM `device_tokens` ORDER BY id");
    
    //Setup notification message
    $body = array();
    $body['aps'] = array('alert' => 'My push notification message!');
    $body['aps']['notifurl'] = 'http://www.myexampledomain.com';
    $body['aps']['badge'] = 1;
    
    //Setup stream (connect to Apple Push Server)
    $ctx = stream_context_create();
    stream_context_set_option($ctx, 'ssl', 'passphrase', 'password_for_apns.pem_file');
    stream_context_set_option($ctx, 'ssl', 'local_cert', 'apns.pem');
    $fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
    stream_set_blocking ($fp, 0); //This allows fread() to return right away when there are no errors. But it can also miss errors during last seconds of sending, as there is a delay before error is returned. Workaround is to pause briefly AFTER sending last notification, and then do one more fread() to see if anything else is there.
    
    if (!$fp) {
        //ERROR
        echo "Failed to connect (stream_socket_client): $err $errstrn";
    } else {
        $apple_expiry = time() + (90 * 24 * 60 * 60); //Keep push alive (waiting for delivery) for 90 days
    
        //Loop thru tokens from database
        while($row = mysql_fetch_array($result)) {
            $apple_identifier = $row["id"];
            $deviceToken = $row["token"];
            $payload = json_encode($body);
            //Enhanced Notification
            $msg = pack("C", 1) . pack("N", $apple_identifier) . pack("N", $apple_expiry) . pack("n", 32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n", strlen($payload)) . $payload;
            //SEND PUSH
            fwrite($fp, $msg); 
            //We can check if an error has been returned while we are sending, but we also need to check once more after we are done sending in case there was a delay with error response.
            checkAppleErrorResponse($fp);
        }
    
        //Workaround to check if there were any errors during the last seconds of sending.
        usleep(500000); //Pause for half a second. Note I tested this with up to a 5 minute pause, and the error message was still available to be retrieved
    
        checkAppleErrorResponse($fp);
    
        echo 'DONE!';
    
        mysql_close($con);
        fclose($fp);
    }
    
    //FUNCTION to check if there is an error response from Apple
    //         Returns TRUE if there was and FALSE if there was not
    function checkAppleErrorResponse($fp) {
    
       //byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID). Should return nothing if OK.
       $apple_error_response = fread($fp, 6);
       //NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait forever when there is no response to be sent.
    
       if ($apple_error_response) {
            //unpack the error response (first byte 'command" should always be 8)
            $error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);
    
            if ($error_response['status_code'] == '0') {
                $error_response['status_code'] = '0-No errors encountered';
            } else if ($error_response['status_code'] == '1') {
                $error_response['status_code'] = '1-Processing error';
            } else if ($error_response['status_code'] == '2') {
                $error_response['status_code'] = '2-Missing device token';
            } else if ($error_response['status_code'] == '3') {
                $error_response['status_code'] = '3-Missing topic';
            } else if ($error_response['status_code'] == '4') {
                $error_response['status_code'] = '4-Missing payload';
            } else if ($error_response['status_code'] == '5') {
                $error_response['status_code'] = '5-Invalid token size';
            } else if ($error_response['status_code'] == '6') {
                $error_response['status_code'] = '6-Invalid topic size';
            } else if ($error_response['status_code'] == '7') {
                $error_response['status_code'] = '7-Invalid payload size';
            } else if ($error_response['status_code'] == '8') {
                $error_response['status_code'] = '8-Invalid token';
            } else if ($error_response['status_code'] == '255') {
                $error_response['status_code'] = '255-None (unknown)';
            } else {
                $error_response['status_code'] = $error_response['status_code'] . '-Not listed';
            }
    
            echo '<br><b>+ + + + + + ERROR</b> Response Command:<b>' . $error_response['command'] . '</b>&nbsp;&nbsp;&nbsp;Identifier:<b>' . $error_response['identifier'] . '</b>&nbsp;&nbsp;&nbsp;Status:<b>' . $error_response['status_code'] . '</b><br>';
            echo 'Identifier is the rowID (index) in the database that caused the problem, and Apple will disconnect you from server. To continue sending Push Notifications, just start at the next rowID after this Identifier.<br>';
    
            return true;
       }
       return false;
    }
    ?>
    

    【讨论】:

    • 嗨。我在我的(工作)推送器类中实现了你的代码。奇怪的是,我从来没有得到苹果的回应。 $apple_error_response 总是错误的。但是有些推送消息已经传递,有些则失败了。你知道为什么我没有得到任何回应吗?
    • 我在大约一年半前发布了这篇文章,不久之后就停止使用推送通知,所以我真的不记得我处理过的所有问题。对于调试,创建您知道工作的令牌列表,然后复制令牌并修改它们以使其失败(因此 5 个已知好和 5 个已知坏)。然后,一旦您在设备上接受了推送通知并验证您收到了它,请禁用该设备上的推送通知,看看当您再次尝试发送到该令牌时会发生什么。还要检查 Apple 的文档,以确保他们没有更改服务器的响应方式。
    • 感谢您的回答。我不得不将睡眠时间增加到一秒钟,然后它(大部分)起作用了。但我仍然不确定这个时候是否会有答案。所以我做了一个循环,每 50 毫秒检查一次答案,如果 5 秒后它根本没有出现,它就会返回。
    • 在提供lalelu...的令牌时,我什至没有收到错误数据?!
    • 迟到了,但我发现只有当你发送一个有效的十六进制值作为推送令牌时,Apple才会告诉你推送令牌是否无效。如果不是,则消息甚至不会被阅读。所以lalelu的值首先需要检查它是否是有效的十六进制(使用PHP的ctype_xdigit
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多