Respuesta de error de lectura de notificaciones mejoradas de Apple con PHP
ios push-notification (2)
Cuando envías una notificación push, hay varios problemas:
Si hay un problema, Apple lo desconectará pero usted no lo sabe. Cuando utiliza notificaciones básicas, no hay forma de saber si todas se enviaron o no. SOLUCIÓN: Este es el punto central de usar una notificación mejorada y luego verificar si hay una respuesta de error. Tenga en cuenta que usaremos "ORDER BY id" en la consulta de la base de datos y luego usaremos el id como el identificador que enviamos en la notificación. De esta manera, si hay un problema, sabemos exactamente qué fila en la base de datos causó el problema (y, por lo tanto, sabemos cuándo Apple nos desconectó y dejó de enviar las notificaciones). Luego podemos continuar enviando notificaciones Push a todas las filas después de la fila que causó el problema, sin tener que reenviar a las que ya enviamos.
Apple NO envía ninguna respuesta de vuelta si todo está bien, así que esto puede hacer que su secuencia de comandos se detenga y espere para siempre mientras fread () está esperando datos que no llegan. SOLUCIÓN: es necesario establecer stream_set_blocking en 0 para que el fread siempre vuelva de inmediato. Tenga en cuenta que esto causa otro problema menor que el fread puede devolver antes de que reciba una respuesta de error, pero vea la solución en el código, que es solo una pausa de 1/2 a segundo DESPUÉS de que se realice todo el envío y luego verifique el fread una vez más .
Puede enviar varias notificaciones push mucho más rápido de lo que tarda una respuesta de error para responderle. SOLUCIÓN: Nuevamente, esta es la misma solución mencionada anteriormente ... pausar por 1/2 un segundo DESPUÉS de que se realice todo el envío y luego verifique fread una vez más.
Aquí está mi solución usando PHP, que aborda todos mis problemas que encontré. Es bastante básico pero hace el trabajo. Lo he probado enviando algunas notificaciones a la vez y enviando 120,000 a la vez.
<?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;
}
?>
En PHP, ¿cómo usa fread () para verificar si hay una respuesta de error al enviar notificaciones push mejoradas?
He leído los documentos de Apple, un par de publicaciones vagas a través de Google y un par de preguntas / respuestas aquí en SO, pero esto todavía era muy confuso.
Esto es lo que miré: http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html Error de lectura de Apple notificación de inserción con PHP iPhone Push Notificación - Respuesta de error problema
Voy a responder a mi propia pregunta a continuación, basada en el hecho de que: (1) encontré este tema muy confuso, y (2) tuve que juntar la información con mucha prueba y error para que funcionara, y (3) esta publicación de blog que dice que se recomienda: http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/
No estoy seguro de que el contenido de su código, pero debe probar ApnsPHP , está bien probado, funciona perfectamente bien y es capaz de manejar todas las posibles excepciones y errores.
Otras alternativas
https://github.com/sebastianborggrewe/PHP-Apple-Push-Notification-Server https://github.com/bortuzar/PHP-Mysql---Apple-Push-Notification-Server
He probado 2 de los 3 ejemplos y no tuve problemas de implementación y gestión de errores.
Gracias
:)