iPhone OS 3.0一個引入注目的新特性是push notifications(推送通知),它允許向已安裝相關應用程式的各裝置直接發送消息。蘋果在新聞提示或IM應用中展示了此特性,它也十分完美地适合于我們的伺服器監視服務程式Server Density。

我們的程式提供一個選項,當你設定的某個伺服器事件發生時,通知會直接發送到你的iPhone上。這是非常有用的因為它提醒使用者立即打開我們的程式檢視引起此警示的伺服器詳情。
Apple提供了有關實作和處理裝置上提示消息的 iPhone OS 的詳細代碼文檔 ,但它隻包括消息提供者伺服器端程式設計指南。
作為消息提供者,我們需要與 Apple推送通知服務 (APNS)連接配接以發送消息到iPhone。為減少電池使用,一個裝置僅需維持與APNS的一個連接配接。
本教程将從代碼的層面介紹關于怎樣建立一個推送通知伺服器以連接配接APNS并使用推送通知到我們的伺服器監視iPhone程式上。我們是使用PHP進行開發的,我們的示例都是PHP 5相容的。
基本結構
使用唯一的SSL許可證連接配接到APNS
- 循環通過你需要發送到消息
- 為各消息建構有效載荷
- 斷開與 APNS的連接配接
遠端通知資料的流程是單向的。提供者将包括客戶程式裝置令牌和有效載荷的資料打包,發送到APNS,然後APNS再将通知發送給最終裝置。
- Apple 文檔
限制
- 有效載荷限制為256位元組 – 它包括了消息主體以及你希望傳送帶其他屬性。推送通知并不适于傳送大量的資料。例如,我們僅僅傳送一條短消息通知伺服器監視的事件已經被觸發了。
- APNS并不提供消息發送成功與否的回饋狀态。一個原因是如果一個裝置無法聯系那麼發送給它的消息将被存于隊列中,然而隻有最新發送的消息被存于隊列中 – 覆寫了先前發送但不成功的消息。
- 推送通知不适合用于發送緊急通知,因為消息僅在裝置具有wifi或手機服務連接配接的情況下才能被發送,這也是為什麼我們推薦與其它方法如email或SMS一起使用的原因。
- 用來與APNS通訊的SSL許可證(下面将讨論)是在程式層生成的。本教程涉及到實作方法僅适于單個iPhone程式,是以如果你有多個程式,那麼你需要修改代碼使之适合于使用多個許可證。
裝置令牌
每條推送消息都必須針對某特定裝置。這是通過使用在你的iPhone程式中由APNS産生的唯一deviceToken(裝置令牌)來實作的。一旦擷取了此令牌,你需要将其存儲于伺服器而不是你的iPhone程式内。它看上去像這樣:
c9d4c07c fbbc26d6 ef87a44d 53e16983 1096a5d5 fd825475 56659ddd f715defc
在我們的 Server Density iPhone 程式中,我們在程式啟動時調用相應的令牌生成方法,然後通過 HTTP API 調用 傳回給我們的伺服器 。這将使得deviceToken存儲于伺服器的有關使用者的資料庫中,進而我們可以使用它與持有此裝置的使用者進行通訊。
回報服務
Apple 還提供了一個 回報服務 ,你應該定期查詢。它提供了一個以前使用過但不再有效的(例如使用者解除安裝了你的iPhone程式)裝置令牌清單。你可以從你的資料庫中删除這些裝置令牌。
本教程不涉及回報服務的使用。
許可證
要進行推送服務的第一件事就是擷取推送許可證。它用來對你通過SSL與APNS通訊進行識别。
在Mac上生成 Apple推送通知SSL許可證:
- 登入到 iPhone Developer Connection Portal 并點選 App IDs
- 建立一個不使用通配符的 App ID 。通配符 ID 不能用于推送通知服務。例如,我們的iPhone程式ID像這樣: AB123346CD.com.serverdensity.iphone
- 點選App ID旁的“Configure”,然後按下按鈕生産 推送通知許可證。根據“向導”指導的步驟生成一個簽名并上傳,最後下載下傳生成的許可證。此步驟在 Apple文檔中 也有談到。
- 通過輕按兩下.cer檔案将你的 aps_developer_identity.cer 引入Keychain中。
- 在Mac上啟動 Keychain助手,然後在login keychain中選擇 Certificates分類。你将看到一個可擴充選項“Apple Development Push Services”
- 擴充此選項然後右擊“Apple Development Push Services” > Export “Apple Development Push Services ID123”。儲存為 apns-dev-cert.p12 檔案。
- 擴充“Apple Development Push Services” 對“Private Key”做同樣操作,儲存為 apns-dev-key.p12 檔案。
- 需要通過終端指令将這些檔案轉換為PEM格式:
openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12 openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
- 如果你想要移除密碼,要麼在導出/轉換時不要設定或者執行:
openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem
- 最後,你需要将鍵和許可檔案合成為apns-dev.pem檔案,此檔案在連接配接到APNS時需要使用:
cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem
将此檔案儲存為一個易記的名字,你有可能以後會用到它。上述步驟同樣适合于生成産品許可證。
載荷内容
載荷 格式化為遵循 RFC 4627标準的JSON格式。它由以下幾部分組成:
- 提示 – 顯示于裝置上的文本串
- 辨別 – 裝置螢幕中程式圖示上顯示的整數
- 聲音 – 顯示消息在裝置的同時發出的聲音的文本名字
- 本教程僅處理發送簡單提示文本串,但也可以發送包括諸如顯示自定義按鈕等在内的各種選項的字典集。
建立載荷
使用 PHP 很容易根據數組并 轉換成 JSON而建立載荷:
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload = json_encode($payload);
顯示 $payload 的内容可以看到傳送到APNS 的 JSON字元串:
{
"aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" }
}
這将使消息顯示于裝置上,觸發提升聲音并将“1”置于程式圖示上。預設按鈕“Close”和“View”同時會顯示于彈出視窗上。
對于 Server Density iPhone程式而言,讓使用者按下“View”直接進入産生此提示的伺服器是很重要的,是以我們增加了額外的自定義值:
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload['server'] = array('serverId' => $serverId, 'name' => $name);
$output = json_encode($payload);
當使用者按下“View”後,自定義server值将被傳遞到裝置中的程式。JSON 值如下:
{
"aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" },
"server" : { "serverId" : 1, "name" : "Server name")
}
256位元組的限制适用于整個載荷,包括自定義字典集。
原生接口
在Server Density中,一旦産生了一條提示,将建立一個載荷并插入隊列中。是以有必要時我們可以同時發送多個載荷。
Apple推薦使用這種方法,因為如果你在發送各載荷時頻繁連接配接和斷開,APNS有可能會封鎖你的IP。
如Apple 描述:
原生接口使用原生socket,具有二進制内容,采用資料流技術,不産生回饋。
打開連接配接
打開連接配接的 PHP 5代碼如下:
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = 'apns-dev.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2,
STREAM_CLIENT_CONNECT, $streamContext);
如果發送錯誤,你可以參考$errorString。它也包括了SSL許可證不正确時的詳細資訊。
許可證檔案處于執行的PHP代碼的目前工作目錄下,如果需要你可指定其絕對路徑。
注意測試時應該使用開發許可證及sandbox。成品主機名為 gateway.push.apple.com ,而且你必須使用不同的産品許可證。
發送載荷
在此,我們循環整個載荷隊列進行發送。建構發送到APNS的二進制内容簡單示例如下:
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) .
chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
注意 $deviceToken 是從資料庫中提取并去除空格得到的。我們還應該檢查是否$payload超過256個位元組。
$apnsMessage 包括了正确的二進制載荷,而fwrite 将載荷寫入目前活動的資料流連接配接中。
完成後,應關閉連接配接:
socket_close($apns);
fclose($apns);
php-apns
有一個開源伺服器庫php-apns實作了以上所有功能,它依賴于 memcached。我們不想使用任何第三方代碼,是以完全自己編寫了自己的伺服器。我們使用自定義cron系統,幾秒鐘運作一次。