2012-04-07 13 views
16

W PHP, w jaki sposób używasz fread(), aby sprawdzić, czy jest odpowiedź na błąd przy wysyłaniu rozszerzonych powiadomień push?PHP Apple Enhanced Push Notification read error response

Przeczytałem dokumentację Apple, kilka niejasnych postów za pośrednictwem Google i parę pytań/odpowiedzi tutaj na temat SO, ale to wciąż było bardzo mylące.

Oto co wyglądało na: 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

mam zamiar odpowiedzieć na moje własne pytanie poniżej opiera się na fakcie, że: (1) Znalazłem to bardzo mylące tematu, oraz (2) Musiałem zebrać informacje razem z dużą ilością prób i błędów, aby to zadziałało, i (3) ten post na blogu, który mówi, że jest zachęcony: http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

Odpowiedz

35

Po wysłaniu powiadomienia push, istnieje kilka problemów:

  • Jeśli istnieje problem, Apple będzie odłączyć ale nie wiem o tym. Podczas korzystania z podstawowych powiadomień nie ma możliwości sprawdzenia, czy wszystkie zostały wysłane, czy nie. ROZWIĄZANIE: Jest to cały sens używania ulepszonego powiadomienia, a następnie sprawdzania odpowiedzi na błąd. Zwróć uwagę, że użyjemy "ORDER BY id" w zapytaniu do bazy danych, a następnie użyjemy identyfikatora jako identyfikatora, który wysyłamy w powiadomieniu. W ten sposób, jeśli wystąpi problem, dokładnie wiemy, który wiersz w db spowodował problem (i dlatego wiemy, kiedy Apple rozłączył nas i przestał wysyłać powiadomienia). Następnie możemy kontynuować wysyłanie powiadomień push do wszystkich wierszy po wierszu, który spowodował problem, bez konieczności ponownego wysyłania do tych, które już wysłaliśmy.

  • Apple NIE wysyła żadnej odpowiedzi, jeśli wszystko jest w porządku, więc może to spowodować wstrzymanie skryptu i czekać na zawsze, gdy fread() czeka na dane, które nie nadchodzą. ROZWIĄZANIE: Należy ustawić stream_set_blocking na 0, aby fread zawsze zwracał się od razu. Zauważ, że powoduje to inny drobny problem, który może zostać zwrócony, zanim otrzyma odpowiedź o błędzie, ale zobacz obejście tego kodu, które ma na celu zatrzymanie na półtorej sekundy po zakończeniu wysyłania, a następnie sprawdź jeszcze raz. .

  • Możesz wysyłać wiele powiadomień push znacznie szybciej niż otrzymasz odpowiedź o błędzie, aby się z Tobą skontaktować. ROZWIĄZANIE: Ponownie jest to to samo obejście wspomniane powyżej ... pauza przez 1/2 sekundy PO zakończeniu wysyłania, a następnie sprawdź fread jeszcze raz.

Oto moje rozwiązanie z wykorzystaniem PHP, które rozwiązuje wszystkie napotkane przeze mnie problemy. Jest dość prosty, ale wykonuje swoją pracę. Przetestowałem to, wysyłając jednocześnie kilka powiadomień, a także wysyłając 120 000 jednocześnie.

<?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

Witam. Zaimplementowałem Twój kod w mojej (roboczej) klasie pusher. O dziwo, nie dostaję odpowiedzi od jabłka. "$ Apple_error_response" jest zawsze fałszywe. Ale niektóre wiadomości Push są dostarczane, a niektóre po prostu nie. Masz pojęcie, dlaczego nie otrzymałem odpowiedzi? –

+0

Opublikowałem to prawie półtora roku temu i przestałem używać powiadomień push niedługo po tym, więc nie pamiętam wszystkich problemów, z którymi miałem do czynienia. Do debugowania utwórz listę tokenów, o których wiesz, że działa, a następnie skopiuj tokeny i modyfikuj je, aby się nie udało (więc 5 znanych dobrych i 5 znanych złych). Następnie po zaakceptowaniu powiadomienia Push na urządzeniu i sprawdzeniu, czy je otrzymujesz, wyłącz powiadomienia push na tym urządzeniu i zobacz, co się stanie, gdy spróbujesz ponownie wysłać to token. Sprawdź również dokumentację Apple, aby upewnić się, że nie zmieniły się informacje na temat reakcji serwera. – jsherk

+0

Dzięki za odpowiedź. Musiałem zwiększyć czas usypiania do jednej sekundy, a następnie zadziałało (głównie). Ale wciąż nie byłem pewien, czy odpowiedź będzie w tym czasie. Więc zrobiłem pętlę, która sprawdza odpowiedź co 50 milisekund, a jeśli w ogóle nie pojawiła się po 5 sekundach, powraca. –

2

Nie jestem pewien, czy treść twojego kodu, ale powinieneś spróbuj ApnsPHP jest dobrze przetestowany działa idealnie dobrze i jest w stanie obsłużyć wszystkie możliwe wyjątek i błąd dla ciebie.

Inne alternatywy

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

przetestowali 2 z 3 i przykłady i nie nie ma problemu z implementacją i zarządzaniem błędami.

Dzięki

:)

+0

Dzięki za te alternatywy. – jsherk

Powiązane problemy