16

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

私はAppleのドキュメント、Googleからのいくつかのあいまいな投稿、SOに関するいくつかの質問/回答を読みましたが、これはまだ非常に混乱していました.

http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html PHPを 使用した Apple 拡張プッシュ通知からの読み取りエラー iPhone プッシュ通知 - エラー応答問題

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

4

2 に答える 2

35

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

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

  • Apple はすべてが問題なければ応答を返さないため、スクリプトが一時停止し、fread() が来ないデータを待っている間、永遠に待機する可能性があります。解決策: fread が常にすぐに戻るように、stream_set_blocking を 0 に設定する必要があります。これにより、エラー応答を受信する前に 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;
}
?>
于 2012-04-07T22:50:16.703 に答える
2

コードの内容はよくわかりませんが、ApnsPHPを試す必要があります。十分にテストされており、完全に正常に動作し、考えられるすべての例外とエラーを処理できます。

その他の代替手段

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

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

ありがとう

:)

于 2012-04-07T22:22:12.520 に答える