2012-04-07 8 views
16

En PHP, ¿cómo se usa fread() para verificar si hay una respuesta de error al enviar notificaciones push mejoradas?PHP Notificación de actualización de Apple mejorada respuesta de error de lectura

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 han visto: 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

voy a responder a mi propia pregunta a continuación, basado en el hecho de que: (1) Me pareció un tema muy confuso, y (2) Tuve que juntar la información junto con muchas pruebas y errores para que funcionase, y (3) esta publicación de blog que dice que es alentadora: http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

Respuesta

35

Cuando se envía una notificación de inserción, hay varios problemas:

  • Si hay un problema, Apple va a desconectar usted, pero usted no sabe al respecto. Cuando utiliza notificaciones básicas, no hay manera de saber si fueron enviadas o no. SOLUCIÓN: Este es el objetivo de utilizar una notificación mejorada y luego buscar una respuesta de error. Tenga en cuenta que usaremos "ORDER BY id" en la consulta de la base de datos y luego usaremos la identificación como el identificador que enviamos en la notificación. De esta forma, si hay un problema, sabemos exactamente qué fila del archivo db 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 si todo está bien, por lo que puede hacer que su secuencia de comandos se detenga y espere por siempre mientras fread() está esperando datos que no están llegando. SOLUCIÓN: Es necesario establecer stream_set_blocking en 0 para que el fread siempre regrese de inmediato. Tenga en cuenta que esto causa otro problema menor que fread puede devolver antes de que reciba una respuesta de error, pero vea la solución alternativa en el código, que es simplemente pausar 1/2 segundo después de que todo el envío haya finalizado y luego verifique fread una vez más .

  • Puede enviar varias notificaciones de inserción mucho más rápido de lo que tarda una respuesta de error para responderle. SOLUCIÓN: De nuevo, esta es la misma solución mencionada arriba ... pausa por 1/2 segundo DESPUÉS de que todo el envío haya finalizado y luego revise "fread" una vez más.

Aquí está mi solución usando PHP, que se ocupa de todos mis problemas que me encontré. Es bastante básico, pero hace el trabajo. Lo probé 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>&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; 
} 
?> 
+0

Hola. Implementé tu código en mi clase de empujador (en funcionamiento). Extrañamente, nunca recibo una respuesta de Apple. El '$ apple_error_response' siempre es falso. Pero algunos de los mensajes push SON entregados, y algunos simplemente fallan. ¿Tienes alguna idea de por qué no recibo ninguna respuesta? –

+0

Lo publiqué hace casi un año y medio y dejé de usar notificaciones push poco tiempo después, así que realmente no recuerdo todos los problemas que tuve. Para la depuración cree una lista de tokens que sepa que funcionan, copie los tokens y modifíquelos para que fallen (por lo tanto, 5 conocidos y 5 conocidos como malos). Luego, una vez que haya aceptado la notificación de inserción en su dispositivo y haya verificado que la recibió, deshabilite las notificaciones automáticas en ese dispositivo y vea qué ocurre cuando intenta enviar de nuevo ese token. También consulte la documentación de Apple para asegurarse de que no haya cambiado algo sobre cómo responde su servidor. – jsherk

+0

Gracias por su respuesta. Tuve que aumentar el tiempo de sueño a un segundo, luego funcionó (principalmente). Pero todavía no estaba seguro de si la respuesta estaría allí en este momento. Así que hice un ciclo que busca una respuesta cada 50 milisegundos, y si no llega después de 5 segundos, vuelve. –

2

No estoy seguro del contenido de tu código pero debes intente ApnsPHP está bien probado funciona perfectamente bien y es capaz de manejar todo lo posible excepción y error para ti.

Otras Alternativas

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

han puesto a prueba 2 de los 3 i ejemplos e hizo no tiene problema de implementación y gestión de errores.

Gracias

:)

+0

Gracias por estas alternativas. – jsherk

Cuestiones relacionadas