详解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了

时间: 06-19

详解APNS苹果消息推送通知的相关文章

Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送,写这个系列真的很要命,你要去把他们的API文档大致的翻阅一遍,而且各种功能都实现一遍,解决各种bug各种坑,不得不说,极光推送真坑,大家使用还是要慎重,我们看一下极光推送的官网 https://www.jpush.cn/common/ 推送比较使用,很多软件有需要,所以在这个点拿出来多讲讲,我们本节

.net C# 苹果消息推送 工具类

public class AppleAPNSMessage { /// <summary> /// 苹果信息推送 证书 路径(注意测试版跟正式发布版证书上不一样) /// </summary> private static string cerPath = ConfigurationManager.AppSettings["CerPath"]; /// <summary> /// 苹果推送服务 密码 /// </summary> priv

苹果iOS APNS消息推送通知

参考链接: http://www.tairan.com/archives/194 http://www.tairan.com/archives/240 https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ProvisioningDevelopment.html#//apple_ref/doc/uid/TP40008194

使用swoole进行消息推送通知,配合vb.net进行客户端开发一样爽

在以前的项目中,就曾听说过swoole的大名,想用来进行消息推送,但是当时只是有了初步的了解,并不敢大胆的运用到线上产品.所谓 识不足则多虑,威不足则多怒.所以就是怕,只能跟领导说了运用极光的推送功能,而且还说出了一定的理由,领导自然也只有相信了,那就用极光推送吧! 最近闲来无事,又重新温习了一下swoole的消息通知功能,虽然在项目开发当时,也曾实现过简单的操作的,但是这次温习更加深了学习,配合vb.net进行开发,感觉还是棒棒哒,下面是一套实现过程,如有需要参考,请拿去,不谢! 首先,我的开

苹果推送通知服务APNs编程(转)

add by zhj: 下面的几篇文章也非常好, http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1 http://www.raywenderlich.com/32963/apple-push-notification-services-in-ios-6-tutorial-part-2 http://blog.csdn.net/chenglibin1988/art

iOS 消息推送及本地通知,原理解析

在此整理了一些前辈的思路,根据自己对问题的理解及相似方面的比较做了这篇笔记,本文并未详细讲解,只是根据自己提出问题进行一个解决,希望能对大家有些帮助. 1. deviceToken与UUID,UDID的区别 deviceToken : A token that identifies the device to APS. The token is an opaque data type because that is the form that the provider needs to subm

IOS 基于APNS消息推送原理与实现(JAVA后台)--转

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

转:IOS 基于APNS消息推送原理与实现(JAVA后台)

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

iOS 基于APNS消息推送原理与实现(包括JAVA后台代码)

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的