1. 程式人生 > >詳解APNS蘋果訊息推送通知

詳解APNS蘋果訊息推送通知

蘋果訊息通知什麼?

如下圖如示


就是APP的服務端在使用者未開啟APP程序時,還能發通知給使用者APP的服務技術

相關概念

Provider:提供推送的第三方服務系統

Device:蘋果裝置,例如iphone和ipad等

APNS:蘋果推送訊息服務,屬於蘋果的服務

APP:安裝在蘋果裝置上的應用程式

DeviceToken:裝置的標識,用於確定接收通知的裝置及APP

Payload:推送訊息的傳輸形式

總體示意圖

從上圖可以看出Provider與APNS之間是要建立連線的,APNS和Device之間也是要建立連線的,這兩個連線都是加密的,採用的TLS的方式,加密用的證書是在蘋果官方網站上購買生成的,這個證書分兩種,一種是“開發者證書”,使用開發者證書的APP可以釋出在APPSTORE上面,開發者證書的使用費是每年99美元,另一種是“企業證書”,使用企業證書的APP只能發在企業內部市場,不能發在APPSTORE上,企業證書的年費是299美元,企業證書對安裝人數是沒有限制的,但開發者證書對APP的安裝人數是有限制的,但從APPSTORE上安裝則沒有限制。

安全連線的建立方式如圖:


如何獲取確定接收者的DeviceToken?

Provider如何取得DeviceToken呢?具體如圖所示:


獲取是由APP通過註冊服務後取得DeviceToken,然後傳給Provider。由於這個DeviceToken是會變的,所以最好是APP在每次啟動時都能註冊一下,獲取最新的DeviceToken

IOS下的註冊獲取DeviceToken的程式碼官方給出如下:

- (void)applicationDidFinishLaunching:(UIApplication *)app {// other setup tasks here....
[[UIApplication sharedApplication]
registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound)];
}
// Delegation methods
- (void)application:(UIApplication *)app
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const void *devTokenBytes = [devToken bytes];
self.registered = YES;
[self sendProviderDeviceToken:devTokenBytes]; // custom method
}
- (void)application:(UIApplication *)app
didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"Error in registration. Error: %@", err);
}

如何傳送通知訊息?

Provider和APNS之間是一個安全的Socket連線,傳輸的是規定格式的二進位制資料,如下形式


當然,這個就是TCP報文,然而具體的報文又有好幾種,有傳送普通訊息的傳輸形式,有獲取錯誤響應的傳輸形式,還有獲取反饋的報文形式等,對於普通的發訊息的報文格式如圖如示:

最前面1個位元組是命令型別,2-3位元組是DeviceToken的長度,後面是DeviceToken的具體值,後面是訊息的長度和訊息的具體內容。發完訊息後,在關閉Socket前還可以檢視錯誤的響應,以便確認訊息是否成功地發給APNS,錯誤響應的報文如下:


命令為8的表示是錯誤響應的報文,Status是錯誤碼,Identifier是用於定位具體的哪條推送訊息,這個是增強型通知裡的傳入的值,首先看下蘋果都有哪些返回錯誤碼:


再看下Identifier傳入使用的增強型的通知報文格式:


官方給出傳送普通訊息的C++程式碼如下:

static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff, size_t payloadLength){
    bool rtn = false;
    if (sslPtr && deviceTokenBinary && payloadBuff && payloadLength){
	uint8_t command = 0; /* command number */
	char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint16_t) +
	DEVICE_BINARY_SIZE + sizeof(uint16_t) + MAXPAYLOAD_SIZE];
	/* message format is, |COMMAND|TOKENLEN|TOKEN|PAYLOADLEN|PAYLOAD| */
	char *binaryMessagePt = binaryMessageBuff;
	uint16_t networkOrderTokenLength = htons(DEVICE_BINARY_SIZE);
	uint16_t networkOrderPayloadLength = htons(payloadLength);
	/* command */
	*binaryMessagePt++ = command;
	/* token length network order */
	memcpy(binaryMessagePt, &networkOrderTokenLength, sizeof(uint16_t));
	binaryMessagePt += sizeof(uint16_t);
	/* device token */
	memcpy(binaryMessagePt, deviceTokenBinary, DEVICE_BINARY_SIZE);
	binaryMessagePt += DEVICE_BINARY_SIZE;
	/* payload length network order */
	memcpy(binaryMessagePt, &networkOrderPayloadLength, sizeof(uint16_t));
	binaryMessagePt += sizeof(uint16_t);
	/* payload */
	memcpy(binaryMessagePt, payloadBuff, payloadLength);
	binaryMessagePt += payloadLength;
	if (SSL_write(sslPtr, binaryMessageBuff, (binaryMessagePt - binaryMessageBuff)) > 0)
            rtn = true;
    }
    return rtn;
}

傳送增強的通知訊息的程式碼:

static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff, size_t payloadLength){
    bool rtn = false;
    if (sslPtr && deviceTokenBinary && payloadBuff && payloadLength){
	uint8_t command = 1; /* command number */
	char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + DEVICE_BINARY_SIZE + sizeof(uint16_t) + MAXPAYLOAD_SIZE];
	/* message format is, |COMMAND|ID|EXPIRY|TOKENLEN|TOKEN|PAYLOADLEN|PAYLOAD|*/
	char *binaryMessagePt = binaryMessageBuff;
	uint32_t whicheverOrderIWantToGetBackInAErrorResponse_ID = 1234;
	uint32_t networkOrderExpiryEpochUTC = htonl(time(NULL)+86400); // expire
	message if not delivered in 1 day
	uint16_t networkOrderTokenLength = htons(DEVICE_BINARY_SIZE);
	uint16_t networkOrderPayloadLength = htons(payloadLength);
	/* command */
	*binaryMessagePt++ = command;
	/* provider preference ordered ID */
	memcpy(binaryMessagePt, &whicheverOrderIWantToGetBackInAErrorResponse_ID,
	sizeof(uint32_t));
	binaryMessagePt += sizeof(uint32_t);
	/* expiry date network order */
	memcpy(binaryMessagePt, &networkOrderExpiryEpochUTC, sizeof(uint32_t));
	binaryMessagePt += sizeof(uint32_t);
	/* token length network order */
	memcpy(binaryMessagePt, &networkOrderTokenLength, sizeof(uint16_t));
	binaryMessagePt += sizeof(uint16_t);
	/* device token */
	memcpy(binaryMessagePt, deviceTokenBinary, DEVICE_BINARY_SIZE);
	binaryMessagePt += DEVICE_BINARY_SIZE;
	/* payload length network order */
	memcpy(binaryMessagePt, &networkOrderPayloadLength, sizeof(uint16_t));
	binaryMessagePt += sizeof(uint16_t);
	/* payload */
	memcpy(binaryMessagePt, payloadBuff, payloadLength);
	binaryMessagePt += payloadLength;
	if (SSL_write(sslPtr, binaryMessageBuff, (binaryMessagePt - binaryMessageBuff)) > 0)
	    rtn = true;
  }
  return rtn;
}

角標(Badge)是什麼?

還有一個重要的概念是角標,如下:


這個角標是推送訊息裡傳的,可以指定,這個要看具體的業務怎麼定了,用於提醒使用者未讀訊息個數。

結尾

瞭解了以上原理和過程,相信用什麼語言都可以寫Provider程式了,本人基於java語言也簡單寫了個demo,可以支援多個應用的推送,等完善了就可以放github了

相關推薦

APNS蘋果訊息通知

蘋果訊息通知什麼? 如下圖如示 就是APP的服務端在使用者未開啟APP程序時,還能發通知給使用者APP的服務技術 相關概念 Provider:提供推送的第三方服務系統 Device:蘋果裝置,例如iphone和ipad等 APNS:蘋果推送訊息服務,屬於蘋果的服務 APP

Android安卓狀態列訊息通知(Notification)

我從不猜測,猜測是一個很壞的習慣——會影響正常的邏輯推理能力。              ——阿瑟·柯南·道爾 《福爾摩斯探案集》 近日,在做安卓專案開發的時候涉及到狀態列通知的需求,查了資料,總結一個簡

iOS 通知:本地通知(UILocalNotification)和遠端通知(APNs)

——本地推送通知 推送通知的作用? 在App退到後臺或者完全退出時,可以使用通知來告訴使用者某件事情,比如推送新的聊天訊息、新聞等 通知對應的效果: 在主螢幕的頂端會出現通知訊息 當手機鎖屏時出現在鎖屏介面,可以通過滑動開啟該App, 在通知中心中

蘋果通知服務(APNs)程式設計

iPhone 對於應用程式在後臺執行有諸多限制(除非你越獄)。因此,當用戶切換到其他程式後,原先的程式無法保持執行狀態。對於那些需要保持持續連線狀態的應用程式(比如社群網路應用),將不能收到實時的資訊。 為解決這一限制,蘋果推出了APNs(蘋果推送通知服務)。APNs

IOS APNS訊息框架介紹(pushy)以及詳細使用方法

最近公司需要做IOS訊息推送的功能,我負責後臺推送,IOS端資料處理以及回撥我不負責,本篇文章主要介紹基於java的apns訊息推送,使用框架為pushy。 宣告:我先前也沒有接觸過這個IOS推送,自己研究了兩天,通過百度,對比各個框架的優缺點,最後選擇了這個框架,有說的不對的地方,還

NodeJs 實現IOS APNS 訊息服務

公司的專案要求接入伺服器自己接入原生的ISO 推送服務,不再接第三方的SDK,網上也有很多例子講解什麼是APNS ,如何獲取證書,怎麼接入,剛開始還是聽順利的,就是在獲取pem證書上面有些小問題,不過後來ios開發還是解決了,最後他自己做了一個獲取證書的總結。 我做的就是nodejs 服

iOS整合極光 通知 和 自定義訊息

支援的版本 r1.2.5 以後。 功能說明 只有在前端執行的時候才能收到自定義訊息的推送。 從jpush伺服器獲取使用者推送的自定義訊息的內容、標題、附件欄位等。 實現方法 獲取iOS的推送內容需要在delegate類中註冊通知並實現回撥方法。 1、在方法-

java伺服器端呼叫JPush極光api通知訊息

呼叫JPush推送的api,官網上已經有很詳細的說明了,我只是寫了一個小的demo測試下了! 在極光推送的官網註冊帳號,獲得應用標識(AppKey) , API MasterSecret , apk應用,將apk安裝在手機上並執行, 後臺測試程式碼: package com.zensoftware.

ActiveMQ基本與總結& 訊息佇列-/拉模式學習 & ActiveMQ及JMS學習

ActiveMQ基本詳解與總結 MQ簡介:   MQ全稱為Message Queue, 訊息佇列(MQ)是一種應用程式對應用程式的通訊方法。應用程式通過寫和檢索出入列隊的針對應用程式的資料(訊息)來通訊,而無需專用連線來連結它們。訊息傳遞指

Android訊息(廣播機制)+通知

Android廣播機制使用了觀察著模式; (1) 通知 1) 獲取狀態通知欄管理 NotificationManager 是一個系統Service,所以必須通過getSystemService(NO

iOS開發 - ANPs通知 標簽: 通知ANPs遠程、本地

control con 垃圾 pre 條件 %20 常用 建立連接 mod iOS開發 - ANPs推送通知 標簽: 推送通知ANPs遠程推送本地推送 2015-05-03 14:12 3510人閱讀 評論(0) 收藏 舉報 本文章已收錄於: iOS知識庫

在Android應用程序中實現通知

xamarin android 幾乎每一個應用程序的一個重要特性是支持推送通知的能力。使用推送通知,您可以更新用戶,而不需要應用程序在任何時候運行或輪詢服務器, 避免潛在的電池電量不足。 隨著火力點雲信息的介紹(FCM),谷歌使得在Android應用程序中實現推送通知變

背水一戰 Windows 10 (121) - 後臺任務: 通知

target 聲明 測試 show null result href -c Coding [源碼下載] 背水一戰 Windows 10 (121) - 後臺任務: 推送通知 作者:webabcd介紹背水一戰 Windows 10 之 後臺任務 推送通知

從構建分布式秒殺系統聊聊WebSocket通知

shadow [1] 小夥伴 ref 相關 消費 png 重試 們的 前言 秒殺架構到後期,我們采用了消息隊列的形式實現搶購邏輯,那麽之前拋出過這樣一個問題:消息隊列異步處理完每個用戶請求後,如何通知給相應用戶秒殺成功? 場景映射 首先,我們舉一個生活中比較常見的例子:我

Android 基於Netty的訊息方案之物件的傳遞(四)

在上一篇文章中《Android 基於Netty的訊息推送方案之字串的接收和傳送(三)》我們介紹了Netty的字串傳遞,我們知道了Netty的訊息傳遞都是基於流,通過ChannelBuffer傳遞的,那麼自然,Object也需要轉換成ChannelBuffer來傳遞。好在Netty本身已經給我們寫好了

Android 基於Netty的訊息方案之字串的接收和傳送(三)

在上一篇文章中《Android 基於Netty的訊息推送方案之概念和工作原理(二)》 ,我們介紹過一些關於Netty的概念和工作原理的內容,今天我們先來介紹一個叫做ChannelBuffer的東東。 ChannelBuffer  Netty中的訊息傳遞,都必須以位元

Android 基於Netty的訊息方案之概念和工作原理(二)

上一篇文章中我講述了關於訊息推送的方案以及一個基於Netty實現的一個簡單的Hello World,為了更好的理解Hello World中的程式碼,今天我來講解一下關於Netty中一些概念和工作原理的內容,如果你覺得本篇文章有些枯燥,請先去閱讀《Android 基於Netty的訊息推送方案之Hell

android 實現mqtt訊息,以及不停斷線重連的問題解決

前段時間專案用到mqtt的訊息推送,整理一下程式碼,程式碼的原型是網上找的,具體哪個地址已經忘記了。 程式碼的實現是新建了一個MyMqttService,全部功能都在裡面實現,包括連伺服器,斷線重連,訂閱訊息,處理訊息,釋出訊息等基本操作。 首先新增依賴: dependencies { &

基於Netty實現的Android 訊息(即時通訊)的解決方案

根據Netty框架實現訊息推送(即時聊天)功能. Netty框架,TCP長連線,心跳,阻塞訊息佇列,執行緒池處理訊息傳送, 基於Google ProtoBuf自定義的訊息協議, TCP粘包/拆包.... 客戶端通過TCP連線到伺服器,並建立TCP長連線;當伺服器端收到新訊息後通過TCP連線推送給

百度雲訊息

import com.baidu.yun.core.log.YunLogEvent; import com.baidu.yun.core.log.YunLogHandler; import com.baidu.yun.push.auth.PushKeyPair; import com.baidu.yun