PHP Apple, расширенное push-уведомление, чтение ошибки ответа
В PHP, как вы используете fread(), чтобы проверить, есть ли ответ об ошибке при отправке расширенных push-уведомлений?
Я прочитал документы Apple, пару расплывчатых постов через Google и пару вопросов / ответов здесь, на SO, но это все еще очень сбивало с толку.
Вот что я посмотрел: http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html Ошибка чтения из расширенного push-уведомления Apple с помощью PHP iPhone Push-уведомления - ответ на ошибку проблема
Я собираюсь ответить на свой собственный вопрос ниже, основываясь на том факте, что: (1) я нашел эту тему очень запутанной, и (2) мне пришлось собрать информацию вместе с большим количеством проб и ошибок, чтобы заставить ее работать, и (3) это сообщение в блоге, в котором говорится, что это поощряется: http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/
2 ответа
Когда вы отправляете push-уведомление, возникает несколько проблем:
Если возникнет проблема, Apple отключит вас, но вы об этом не знаете. При использовании базовых уведомлений невозможно узнать, были ли они отправлены или нет. РЕШЕНИЕ. В этом весь смысл использования расширенного уведомления и проверки ответа на ошибку. Обратите внимание, что мы будем использовать "ORDER BY id" в запросе к базе данных, а затем использовать идентификатор в качестве идентификатора, который мы отправляем в уведомлении. Таким образом, если есть проблема, мы точно знаем, какая строка в БД вызвала проблему (и, следовательно, мы знаем, когда Apple отключила нас и прекратила отправлять уведомления). Затем мы можем продолжить отправку Push-уведомлений во все строки после строки, вызвавшей проблему, без необходимости повторной отправки в те, которые мы уже отправили.
Apple НЕ отсылает никаких ответов, если все в порядке, так что это может привести к тому, что ваш скрипт приостановится и будет ждать вечно, пока fread() ожидает данные, которые не приходят. РЕШЕНИЕ: необходимо установить stream_set_blocking в 0, чтобы fread всегда возвращался сразу. Обратите внимание, что это вызывает еще одну незначительную проблему, которую fread может вернуть до того, как получит ответ об ошибке, но посмотрите обходной путь в коде, который заключается в том, чтобы просто сделать паузу на 1/2 секунды ПОСЛЕ всей отправки, а затем еще раз проверить fread,
Вы можете отправить несколько push-уведомлений гораздо быстрее, чем ответное сообщение об ошибке, чтобы вернуться к вам. РЕШЕНИЕ: Опять же, это тот же обходной путь, упомянутый выше... сделайте паузу на 1/2 секунды ПОСЛЕ того, как ваша отправка будет выполнена, и затем проверьте фред еще раз.
Вот мое решение с использованием PHP, которое решает все мои проблемы, с которыми я столкнулся. Это довольно простой, но выполняет свою работу. Я проверил это, отправив несколько уведомлений одновременно, а также отправив 120000 одновременно.
<?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> Identifier:<b>' . $error_response['identifier'] . '</b> 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;
}
?>
Не уверен, что содержание вашего кода, но вы должны попробовать ApnsPHP, он хорошо протестирован, работает отлично и способен обрабатывать все возможные исключения и ошибки для вас.
Другие альтернативы
https://github.com/sebastianborggrewe/PHP-Apple-Push-Notification-Server https://github.com/bortuzar/PHP-Mysql---Apple-Push-Notification-Server
Протестировал 2 из 3 i примеров и не столкнулся с проблемой реализации и управления ошибками.
Спасибо
:)