iPhone推送通知功能分析总结
图中,Provider是指某个iPhone软件的Push服务器。APNS是Apple Push Notification Service(Apple Push服务器)的缩写。
整个过程可以分为三个阶段:
- 第一阶段:Push服务器把要发送的消息、目的iPhone的标识打包一个通知包,发给APNS。通知包使用JSON格式并且每一个通知包限定为256B,因此一个推送的信息是十分有限的。
- 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相同标识的iPhone,并把消息发到iPhone。
- 第三阶段:iPhone把发来的消息传递给相应的应用程序, 并且按照设定弹出Push通知。
多个供应商推送到多个设备的通知
准备:
要开始进行推送服务的开发,就必须访问苹果公司的iphone Developer Program门户。https://developer.apple.com/ios/my/overview/index.action 利用iPhone开发人员凭据登入以便访问此站点。在网站上,可以逐步了解创建一个新应用程序标识符的步骤,此标识符与推送服务相关。生成一个应用程序标识符。
生成一个应用程序标识符
在开发人员门户网站上,单击App IDs,这是打开一个允许创建应用程序标识符的页面。每项推送服务都基于一个独立的标识符,所以必须创建一个标识符并设为允许远程通知。推送服务应用程序不能使用通配符标识符,每个启动了推送服务的应用程序都要求有一个唯一的标识符。生成SSL证书。
生成SSL证书
developer,用于测试 production,用于产品 如果是内部测试,使用developer方式即可。推送服务特有的准备文件。
启用了推送服务的应用程序不能用通配符作为准备文件,必须为该应用程序创建一个单独的准备文件。
注册应用程序
利用一个兼容推送服务的移动准备文件来签署应用程序只是使用推送通知的第一步。该应用程序必须利用iphone的远程通知系统来请求注册自己。使用一个单独的UIApplication调用就可以完成。
[[UIApplication sharedApplication]registerForRemoteNotificationTypes:types]
此调用告知iphone OS,该应用程序要接受推送消息。Types指定应用程序将接受的通知警告类型。iphone提供三种类型:UIRemoteNotificationTypeBadge、UIRemoteNotificationTypeSound和UIRemoteNotificationTypeAlert。
选择要使用的类型,通过|一起使用它们:
types = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert;
通过[[UIApplication sharedApplication] unregisterForRemoteNotifications] 来取消注册。
检索设备令牌:
只有应用程序生成一个设备令牌并将其发送到服务器之后才能接受推送消息。应用程序必须将设备令牌发送到推送实际通知的外设备。
一个令牌与一个设备关联。它与SSL证书结合使用来唯一标识iPhone,并且可以用来将消息发送回相关手机。形式如下:
deviceToken = @"8baa53ca 210fc48d e339f80a 2672e58f 4f86f87f 73695797 b43cde969dffe954";
设备令牌是作为注册的副产品而创建的。接受到注册请求之后,iPhone OS使用一个SSL请求立刻联络APNS。因此要连接因特网,如果没有连接,则注册失败。Iphone将请求转发到APNS并等待返回一个设备令牌。
APNS构建设备令牌并将它返回到iPhone OS,有iphone OS通过应用程序委托回调:
-(void)application:(UIApplication *)applicationdidRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken。
有时,APNS不能创建令牌或设备不能发送请求。通过
-(void)application:(UIApplication *)applicationdidFailToRegisterForRemoteNotificationsWithError:(NSError *)error来处理请求错误。
相应通知:
iPhone使用一系列操作来响应推送通知。当应用程序正在运行时,通知将被直接发送到-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 方法。以JSON格式发送的有效内容将被自动转为NSDictionary,应用程序可以根据需要任意使用有效内容中的信息。当应用程序运行时,不再调用声音、标志或警告。
当应用程序未运行时,iphone执行通知。当警告框出现时,用户可以选择Close关闭这个警告框,也可以选择View启动应用程序。启动后,依然通过-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo的方法回调通知信息。
推送服务器
Provider以一种异步的二进制接口与APNS沟通,这种接口对于Provider来说的高速,高容量的,他使用TCP socket流。
发布的接口是通过访问:gateway.push.apple.com, port 2195。
开发的接口是通过访问:gateway.sandbox.push.apple.com, port 2195
对于每一个接口,你应该使用TLS(或SSL)建立安全通信通道。这些连接所需的SSL证书就是在准备工作中所生成的SSL证书 (如果服务器是运行在MacOS X)。
因此,最简单的provider实现,其实就是通过证书,和苹果服务器建立安全连接(tsl或ssl),通过认证建立连接后,provider将token号、通知内容、通知形式(比如是否弹出提示 窗口、是否发声等)发送给苹果的服务器(apns)。
The Notification Payload格式:
{"aps" : { "alert" : { "body" : "Bob wants toplay poker" }, "badge" : 5, "sound" :"bingbong.aiff"}, "acme1" : "bar", "acme2" : ["bang", "whiz" ] }
通知格式
- (IBAction)push:(id)sender { if(self.certificate == nil) {
return;
}
if(self.deviceToken == nil || self.payload == nil) {
return;
}
NSMutableData *deviceToken = [NSMutableData data];
unsigned value;
NSScanner *scanner = [NSScanner scannerWithString:self.deviceToken];
while(![scanner isAtEnd]) {
[scannerscanHexInt:&value];
value= htonl(value);
[deviceTokenappendBytes:&value length:sizeof(value)];
}
char *deviceTokenBinary = (char *)[deviceToken bytes];
char *payloadBinary = (char *)[self.payload UTF8String];
size_t payloadLength = strlen(payloadBinary);
uint8_t command = 1; /* command number */
char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) +
32 + sizeof(uint16_t) + payloadLength];
char *binaryMessagePt = binaryMessageBuff;
uint32_twhicheverOrderIWantToGetBackInAErrorResponse_ID = 1234;
uint32_t networkOrderExpiryEpochUTC = htonl(time(NULL)+86400); // expire message if not delivered in 1 day
uint16_t networkOrderTokenLength = htons(32);
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, 32);
binaryMessagePt += 32;
/* payload length network order */
memcpy(binaryMessagePt,&networkOrderPayloadLength, sizeof(uint16_t));
binaryMessagePt += sizeof(uint16_t);
/* payload */
memcpy(binaryMessagePt, payloadBinary,payloadLength);
binaryMessagePt += payloadLength;
size_t processed = 0;
OSStatus result = SSLWrite(context, binaryMessageBuff,(binaryMessagePt - binaryMessageBuff),&processed) > 0;
NSLog(@"SSLWrite(): %d %d", result, processed);
}
推送的局限性:推送通知不是都是可靠的,苹果公司不保证传送每个通知以及通知到达的顺序。
反馈服务
苹果公司的反馈服务会报告通知的失败。
发布的接口是通过访问:feedback.push.apple.com, port2196。
开发的接口是通过访问:feedback.sandbox.push.apple.com, port2196。
注:Provider要反复监测苹果的反馈服务,来确保不向已经不存在的设备发送推送通知。