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> 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;
}
?>
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? –
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
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. –