PHP集成APNS推送

概述

APNs推送是IOS App的一個(gè)重要功能走触,一般情況下大部分人都會(huì)采用第三方的集成(極光、個(gè)推等等)刷袍。但是既然是第三方的谷丸,肯定有某些地方不能滿足我們的實(shí)際要求,比如推送的頻率购城、次數(shù)吕座、優(yōu)先級(jí)等其他方面”癜澹基于以上問(wèn)題吴趴,我們還是有必要去搭建一個(gè)自己的推送后臺(tái)的,把所有的可控因素掌握在自己的手里侮攀。

搭建一個(gè)自己的推送后臺(tái)的話锣枝,主要分為推送證書(shū)制作,收集設(shè)備信息,推送通知消息這三個(gè)步驟。

推送證書(shū)制作

推送證書(shū)的制作包括兩個(gè)制作過(guò)程兰英,一個(gè)是在蘋(píng)果開(kāi)發(fā)者后臺(tái)制作推送證書(shū)撇叁,證書(shū)類型選擇Apple Push Notification service SSL (Sandbox & Production),第二個(gè)是基于第一步的證書(shū)生成一個(gè).pem格式的證書(shū)提供給后臺(tái)使用。

第一步在蘋(píng)果開(kāi)發(fā)后臺(tái)制作推送證書(shū)箭昵,網(wǎng)上有大把的文章税朴,在這里就不再詳細(xì)描述了,接下來(lái)的著重描述下生成后臺(tái)使用的證書(shū)過(guò)程家制。

Step1

鑰匙串訪問(wèn)中導(dǎo)出推送證書(shū)的公鑰和私鑰正林,命名為apns_cert.p12apns_key.p12,導(dǎo)出的格式注意選擇p12類型

Step2

apns_cert.p12,apns_key.p12轉(zhuǎn)換成對(duì)應(yīng)的.pem文件,文件命名為apns_cert.pem,apns_key.pem

openssl pkcs12 -clcerts -nokeys -out apns_cert.pem -in apns_cert.p12
openssl pkcs12 -nocerts -out apns_key.pem -in apns_key.p12

Step3

如果在使用的推送的時(shí)候不用輸入密碼颤殴,就要執(zhí)行本步驟觅廓,把a(bǔ)pns_key.pem中的密碼給去掉,建議執(zhí)行,得到新文件apns_key_noencrypt.pem

openssl rsa -in apns_key.pem -out apns_key_noencrypt.pem

Step4

把兩個(gè).pem文件合成一個(gè) 得到最終可供后臺(tái)使用的文件 apns_product.pem

cat apns_cert.pem apns_key_noencrypt.pem > apns_product.pem

得到最終文件之后涵但,我們可以使用以下命令測(cè)試以下我們生成的證書(shū)是否能用,如果能夠輸出SSL-Session的鏈接信息的話杈绸,就說(shuō)明我們的證書(shū)沒(méi)問(wèn)題了

openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert apns_product.pem

收集設(shè)備信息

當(dāng)我們需要去給一個(gè)設(shè)備進(jìn)行推送消息的時(shí)候,我們需要知道該設(shè)備的一個(gè)標(biāo)識(shí)矮瘟,這個(gè)標(biāo)識(shí)就是deviceToken了瞳脓。deviceToken是一個(gè)64位長(zhǎng)度的一個(gè)字符串,這個(gè)標(biāo)識(shí)對(duì)于同一個(gè)設(shè)備的不用APP都是不同的澈侠。并且即使是同一個(gè)設(shè)備的同一個(gè)APP而言劫侧,如果APP反復(fù)卸載安裝,這個(gè)標(biāo)示也有可能發(fā)生變化哨啃。因此烧栋,一般來(lái)說(shuō)我們應(yīng)該在APP啟動(dòng)的過(guò)程中去收集deviceToken,然后傳給服務(wù)器保存起來(lái)拳球,服務(wù)器根據(jù)當(dāng)前用戶情況去選擇是否更新用戶的設(shè)備信息审姓。

首先先去蘋(píng)果服務(wù)器注冊(cè)deviceToken

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

//注冊(cè)遠(yuǎn)程通知類型
UIUserNotificationSettings *sting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:sting];
[application registerForRemoteNotifications];
}

當(dāng)deviceToken注冊(cè)成功之后,我們可以在這個(gè)方法里面獲取到deviceToken

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
//把deviceToken存起來(lái)發(fā)給服務(wù)器
}

當(dāng)deviceToken注冊(cè)失敗后祝峻,我們可以再這個(gè)方法里面獲取失敗的信息

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
//失敗的信息
}

當(dāng)APP接收到遠(yuǎn)程通知時(shí)魔吐,需要根據(jù)APP的三種狀態(tài)進(jìn)行不同的處理

1.APP未啟動(dòng)

這種情況下當(dāng)點(diǎn)擊通知欄進(jìn)入到APP的時(shí)候會(huì)在調(diào)用(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions時(shí)將通知消息放在launchOptions中,而通過(guò)點(diǎn)擊APP圖標(biāo)進(jìn)入應(yīng)用程序時(shí)莱找,該字典是空的酬姆。

2.APP啟動(dòng)在后臺(tái)

這種情況下點(diǎn)擊通知欄進(jìn)入APP時(shí)會(huì)調(diào)用(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler,通知消息會(huì)放在userInfo中

3.APP啟動(dòng)在前臺(tái)

這種情況下接收到遠(yuǎn)程通知時(shí)宋距,并沒(méi)有通知欄的提示和提示聲音轴踱,只能自己去在當(dāng)前界面彈框提示或者在某個(gè)地方顯示一個(gè)數(shù)字標(biāo)示表示有新消息,并且會(huì)自動(dòng)調(diào)用(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler方法谚赎。由于第二種和第三種情況都會(huì)調(diào)用該方法淫僻,所以我們需要在這個(gè)方法里面去根據(jù)APP在前臺(tái)還是后臺(tái)做不同的處理。

推送通知消息

到這一步壶唤,我們的準(zhǔn)備工作就都做好了雳灵,接下來(lái)就可以去寫(xiě)后端的推送邏輯了。需要注意一點(diǎn)的是闸盔,蘋(píng)果提供的APNs推送服務(wù)器有兩個(gè)悯辙,一個(gè)是開(kāi)發(fā)環(huán)境的,一個(gè)是正式環(huán)境的,要根據(jù)實(shí)際情況選擇相應(yīng)的服務(wù)器躲撰。

后臺(tái)測(cè)試推送代碼

<?php

//推送目標(biāo)設(shè)備號(hào)(測(cè)試環(huán)境和正式環(huán)境不一樣)
$deviceToken = '4f707f4eb373dfbad83188ae1c71ce3dd9eba983b8234b777a24157df20915f4';

//證書(shū)路徑
$pem = dirname(__FILE__) . '/' . 'apns-product.pem';

//測(cè)試服務(wù)器
$apnsHost = 'ssl://gateway.sandbox.push.apple.com:2195';

//正式服務(wù)器
//$apnsHost = 'ssl://gateway.push.apple.com:2195';

$content = '測(cè)試推送內(nèi)容';
$body = array("aps" => array("alert" => $content,"badge" => 5,"sound"=>'default'),'url'=>'http://keeper.fxtrip.com/fun/index?t=12');

$ctx = stream_context_create();
stream_context_set_option($ctx,"ssl","local_cert",$pem);

$pass = "";     //如果有密碼的話
stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);

$fp = stream_socket_client($apnsHost, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
echo "Failed to connect $err $errstr";
return;
}
print "Connection OK\n";
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack("H*", str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
echo "sending message :" . $payload ."\n";
fwrite($fp, $msg);
fclose($fp);

如果沒(méi)有什么問(wèn)題的話针贬,執(zhí)行完這段代碼我們就能在手機(jī)上看到推送了。

注意:上述示例程序僅適用于測(cè)試推送拢蛋,所以在建立連接后只發(fā)送了一個(gè)推送就關(guān)閉了這個(gè)鏈接桦他。如果在實(shí)際環(huán)境中推送的話,我們不可能針對(duì)每一個(gè)推送都去建立一個(gè)鏈接谆棱,即使你這么干了的話快压,在推送的數(shù)量級(jí)別上去了之后可能會(huì)被蘋(píng)果官方當(dāng)做DDOS攻擊。正確的做法是在建立連接后依次寫(xiě)入多條推送的內(nèi)容

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末垃瞧,一起剝皮案震驚了整個(gè)濱河市蔫劣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌个从,老刑警劉巖脉幢,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異信姓,居然都是意外死亡鸵隧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)意推,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豆瘫,“玉大人,你說(shuō)我怎么就攤上這事菊值⊥馇” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵腻窒,是天一觀的道長(zhǎng)昵宇。 經(jīng)常有香客問(wèn)我,道長(zhǎng)儿子,這世上最難降的妖魔是什么瓦哎? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮柔逼,結(jié)果婚禮上蒋譬,老公的妹妹穿的比我還像新娘。我一直安慰自己愉适,他們只是感情好犯助,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著维咸,像睡著了一般剂买。 火紅的嫁衣襯著肌膚如雪惠爽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,696評(píng)論 1 312
  • 那天瞬哼,我揣著相機(jī)與錄音婚肆,去河邊找鬼。 笑死倒槐,一個(gè)胖子當(dāng)著我的面吹牛旬痹,可吹牛的內(nèi)容都是我干的附井。 我是一名探鬼主播讨越,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼永毅!你這毒婦竟也來(lái)了把跨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沼死,失蹤者是張志新(化名)和其女友劉穎着逐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體意蛀,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耸别,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了县钥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秀姐。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖若贮,靈堂內(nèi)的尸體忽然破棺而出省有,到底是詐尸還是另有隱情,我是刑警寧澤谴麦,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布蠢沿,位于F島的核電站,受9級(jí)特大地震影響匾效,放射性物質(zhì)發(fā)生泄漏舷蟀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一面哼、第九天 我趴在偏房一處隱蔽的房頂上張望野宜。 院中可真熱鬧,春花似錦精绎、人聲如沸速缨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旬牲。三九已至仿粹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間原茅,已是汗流浹背吭历。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留擂橘,地道東北人晌区。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像通贞,于是被迫代替她去往敵國(guó)和親朗若。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容