web-dev-qa-db-ja.com

PHP Apple拡張プッシュ通知読み取りエラー応答

PHPでは、fread()を使用して、拡張プッシュ通知を送信するときにエラー応答があるかどうかをどのように確認しますか?

Appleのドキュメント、Googleによるいくつかの漠然とした投稿、およびSO)に関するいくつかの質問/回答を読みましたが、それでも非常に混乱していました。

これが私が見たものです: http://developer.Apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html=からの読み取りエラーApple PHPで強化されたプッシュ通知iPhoneプッシュ通知-エラー応答の問題

以下の事実に基づいて、私自身の質問に答えます。(1)これは非常に紛らわしいトピックであり、(2)情報を機能させるには、多くの試行錯誤を重ねて情報をつなぎ合わせる必要がありました。 (3)奨励されていると述べているこのブログ投稿: http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

16
jsherk

プッシュ通知を送信する場合、いくつかの問題があります。

  • 問題がある場合、Appleは接続を切断しますが、それについてはわかりません。基本的な通知を使用する場合、すべてが送信されたかどうかを知る方法はありません。解決策:これは、拡張通知を使用してからエラー応答をチェックすることの要点です。データベースクエリで「ORDERBY id」を使用し、通知で送信する識別子としてidを使用することに注意してください。 。このようにして、問題が発生した場合、データベース内のどの行が問題の原因であるかを正確に把握できます(したがって、Appleが切断され、通知の送信が停止された時期がわかります)。その後、続行できます。問題の原因となった行の後のすべての行にプッシュ通知を送信します。既に送信した行に再送信する必要はありません。

  • すべてに問題がない場合、Appleは応答を返しません。そのため、fread()が受信しないデータを待機している間、スクリプトが一時停止して永久に待機する可能性があります。 SOLUTION: freadが常にすぐに戻るように、stream_set_blockingを0に設定する必要があります。これにより、エラー応答を受信する前にfreadが戻る可能性があるという別の小さな問題が発生することに注意してください。ただし、コードの回避策を参照してください。すべての送信が完了した後、1/2秒間一時停止してから、もう一度freadを確認します。 。

  • エラー応答が返ってくるよりもはるかに速く複数のプッシュ通知を送信できます。 解決策:これも上記と同じ回避策です...すべての送信が完了した後、1/2秒間一時停止してから、もう一度フレッドを確認してください。

これが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;
}
?>
35
jsherk

コードの内容がわからない場合は、試してみてください ApnsPHP 十分にテストされており、完全に正常に機能し、考えられるすべての例外とエラーを処理できます。

他の選択肢

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

3つのiの例のうち2つをテストしましたが、実装とエラー管理の問題はありませんでした。

ありがとう

:)

2
Baba