2012-04-07 91 views
16

在PHP中,如何在發送增強型推送通知時使用fread()來檢查是否存在錯誤響應?PHP Apple增強推送通知讀取錯誤響應

我已經閱讀了蘋果文檔,通過谷歌的幾個模糊的帖子,以及這裏的幾個問題/答案,但這仍然是非常混亂。

這是我看了看: 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

我將在下面回答我的問題,基於這樣一個事實:(1)我發現這是一個非常令人困惑的話題,( 2)我不得不用大量的試驗和錯誤的拼湊的信息來得到它的工作,和(3)本博客文章,說是鼓勵:http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

回答

35

當您發送推送通知,有幾個問題:

  • 如果有問題,蘋果將斷開你,但你不知道關於它。當您使用基本通知時,無法知道它們是否全部發送。 解決方案:這是使用增強通知然後檢查錯誤響應的全部要點。請注意,我們將在數據庫查詢中使用「ORDER BY id」,然後使用該ID作爲我們在通知中發送的標識符。這樣,如果出現問題,我們確切知道數據庫中的哪一行導致了問題(因此我們知道Apple何時斷開連接並停止發送通知)。然後,我們可以繼續將推送通知發送到導致問題的行後面的所有行,而不必重新發送到已發送給我們的行。

  • 如果一切正常,Apple不會發送任何迴應,因此,當fread()正在等待未到達的數據時,可能會導致腳本暫停並等待一段時間。 解決方案:需要將stream_set_blocking設置爲0,以便fread總是立即返回。請注意,這會導致fread在接收到錯誤響應之前返回的另一個小問題,但請在代碼中查看解決方法,該代碼僅暫停1/2秒後完成所有發送,然後再次檢查fread 。

  • 您可以發送多個推送通知,比發送錯誤響應要快得多。 解決方案:再次,這是上述相同的解決方法...暫停1/2秒後,所有的發送完成,然後再檢查一次fread。

這是我使用PHP的解決方案,它解決了我遇到的所有問題。它非常基本,但完成了工作。我已經通過一次發送幾個通知來測試它,並且一次發送了12萬個通知。

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

嗨。我實現了你在我的(工作)推動者類中的代碼。奇怪的是,我從來沒有得到蘋果的迴應。 $ apple_error_response始終是false。但是一些推送消息被傳遞,有些只是失敗。你有什麼想法,爲什麼我沒有得到任何迴應? – 2013-09-16 12:54:43

+0

我這將近一年發佈半前和使用推送通知後沒多久,所以我真的不記得我所討論的問題停止。爲了調試創建你知道的令牌列表,然後複製令牌並進行修改,以便它們失敗(所以5已知好&5已知壞)。然後,一旦您接受設備上的推送通知並確認已收到推送通知,請禁用該設備上的推送通知,並查看當您嘗試再次發送該令牌時會發生什麼。同時檢查Apple的文檔以確保他們沒有改變他們的服務器響應方式。 – jsherk 2013-09-16 17:05:44

+0

感謝您的回答。我不得不把睡眠時間增加到一秒鐘,然後它(大部分)工作。但我仍然不確定在這個時候答案會在那裏。所以我做了一個循環,每50毫秒檢查一次答案,如果5秒後它完全沒有響應,它將返回。 – 2013-09-17 07:59:17

2

我不知道你們中的內容,但你應該嘗試ApnsPHP它是經過充分測試的作品非常好,它能夠處理所有可能的異常和錯誤。

其他代替

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

已測試2出了3倍I的例子和做沒有實施和錯誤管理的問題。

感謝

:)

+0

感謝這些替代​​品。 – jsherk 2012-04-08 14:59:19