本章描述消息提供方用于和APNs通讯的接口并且论述消息提供方需要实现的功能。
普通消息提供方的要求
作为一个消息提供方和APNs通讯需要实现一个二进制接口。这是一个高速度,高容量的接口,它利用了 TCP 套接字流技术承载二进制数据。这个二进制进口是异步的。
本二进制接口在生产环境通过 gateway.push.apple:2195提供服务;开发环境通过 gateway.sandbox.push.apple: 2195 提供服务。你可以建立多个并行的连接到一个或多个网关的实例。
对每个接口都应该使用TLS(或SSL)创建一个安全的通讯信道。这些连接的SSL 许可证书通过 iOS Provisioning Portal提供。(在这个连接“Provisioning and Development”查看详情。)创建一个可信任的消息提供者身份标识,应该在连接APNs时提供此证书用于对等网络认证。
注意:创建一个和 APNs的 TLS 会话,必须有一个可信的安全的CA根证书安装在消息提供方服务器上。如果服务运行在 Mac OS X 系统上,那么根证书已经在密钥链内。对于其他系统,证书可能没有被提供。需要从可信SSL证书站点下载。
也应该保留和 APNs 的连接贯穿多个通知消息。APNs 可能认定不断重复建立断开连接是拒绝服务攻击。由于上述问题, APNs 会关闭发生此种情况的连接。
作为消息提供方,应该在以下各方面对推送消息负责:
∙必须生成有效消息体 (参见连接“The Notification Payload”)。
∙需要提供在应用图标上显示的标记数字。
∙应该定时连接反馈服务器并获取多次报告转发失败的设备列表。然后应该停止向这些应用关联的设备发送通知。更多信息参见“The Feedback
Service”。
如果需要支持多语种通知消息,而不使用客户端消息字典的 loc-key 和
loc-args 属性获取本地化提示字符串,就需要在服务器端本地化提示消息的文本。要做到这一点,就需要从客户端应用确定当前选择的语言。“Scheduling, Registering, and Handling Notifications”这个连接提供了一些获取这些信息的建议。关于loc-key 和 loc-args 属性的信息参见“The Notification Payload”。
接口和通知格式
本接口对二进制数据流使用了普通的TCP套接字。为了获得最佳性能,应该在一次传输中批量处理多个通知,或者使用TCP/IP Nagle算法。
接口支持两种格式的通知信息包,一个是简单格式,一个是解决了一些简单格式存在问题的增强格式:
∙通知到期。APNs 有存储转发机制以保证最近的通知能发送到设备上的应用。如果在消息传送时,设备是离线状态,APNs 将在设备下次上线时传
输该消息。使用简单型格式,通知仅被传送而无法关注相关性。换句话说,这通知随着时间过去可能变得没有意义。增强型格式包括一个到期值标明了一个通知的有效期。APNs 在存储转发时丢弃过期的通知。
∙错误响应。使用简单型格式,如果你发送了某种错误的通知消息包——例如,超过约定限制——APNs 会断开连接。没有拒绝这个通知的提示。增强型格式允许一个包含任意标识的提供方标签的消息。如果存在错误,
APNs 返回一个有关标识符和和错误码的包。这个响应可以让消息提供方
定位和改正错误通知。
建议多数消息提供方使用增强型格式。
我们首先分析简单型格式,因为多数格式和增强型共享。图 5-1 说明了这个格式。
图 5-1简单通知格式
简单型格式第一位是命令值 0 。设备令牌和有效消息体的长度必须是在网络传输字节顺序(高位)。另外,应该把设备令牌编码为二进制格式。有效消息体不能超过256字节并且结尾不能是空。
代码清单 5-1 给出了一个使用简单型格式通过二进制接口向 APNs 发送推送消息的例子。这个例子假设已经通过SSL并行认证连接到
gateway.push.apple(或gateway.sandbox.push.apple)。
代码清单 5-1通过二进制接口发送一个简单型格式的通知
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;
}
图 5-2 描述了增强型格式通知包。使用增强型格式,如果 APNs 接收到一个未知命令,将在断开连接前返回一个错误响应。
图 5-2增强型通知格式
增强型通知格式第一字节是命令值 1。增强型通知格式里的两个新字段是标识符和过期值。(其他部分和简单型通知格式一致。)
苹果apple id注册∙标识符——标识此通知的任意值。如果APNs不能解析一个通知则返回包含该标识符的一个错误包。
∙过期——在一个通知失效并且可以丢弃的时的一个可精确到秒的固定UNIX 纪时日期表达式(UTC)。过期值应在在网络传输字节顺序(高位)。
如果过期值是正数 APNs 至少发送一次。可以指定为 0 或者一个小于 0 的值请求 APNs 不存储通知。
如果发送给 APNs 一个错误或未知的通知,它将在断开前返回一个错误响应包。(如果没有错误, APNs 将不返回响应。)图 5-3 描述错误响应包格式。
图 5-3错误响应包格式
通知包由8个一个字节长的命令状态码之一和由消息提供方指定的同一通知的标识符组成。表 5-1 罗列了状态码的可能取值和含义。
表 5-1错误响应包代码
状态码描述
0 正确
1 处理过程错误
2 缺少设备令牌
3 缺少主题
4 缺少有效消息体
5 令牌长度错误
6 大小错误
7 消息体长度错误
8 令牌错误
255 未知错误
清单 5-2 修改了代码清单 5-1中的代码组成一个增强型格式推送通知并发送到 APNs 。与前面的例子一样,假设事先已经通过了并行认证通过 SSL 连接到gateway.push.apple (或 gateway.sandbox.push.apple )。
清单 5-2通过二进制接口发送一个增强型格式通知
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));
发布评论