PHP Apple增强推送通知读取错误响应

时间:2012-04-07 22:06:14

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

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

我已经阅读了Apple文档,通过谷歌发布了几个含糊不清的帖子,以及一些关于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)这篇博文说鼓励:http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

2 个答案:

答案 0 :(得分:35)

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

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

  • 如果一切正常,Apple不会发回任何回复,因此这会导致脚本暂停并等待fread()等待未来的数据。 SOLUTION:需要将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;
}
?>

答案 1 :(得分:2)

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

其他替代方案

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

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

由于

:)