消息通知分本地通知和远程推送通知,是没有运行在前台的应用程序可以让它们的用户获得相关消息通知的方式。消息通知可能是一条消息,即将发生的日历事件,或远程服务器的新数据。当被操作系统显示时,本地通知和推送通知看起来一样。它们可以显示一个警告信息或在应用程序的图标上面显示一个徽标。它们也可以在警告窗或徽标显示时播放一段声音。推送通知是在 iOS 3.0 和 Mac OS X v7.0 之后引入的。本地通知是在 iOS 4.0 之后引入的。它们都不支持 Mac OS X,当用户被通知相应的应用程序有消息,事件,或其他数据时,他们可以启动该应用程序并查看详情。他们也可以选择忽略通知,此时应用程序没有被激活。
本地通知和推送通知为不同的需求而设计的。本地通知是本地 iPhone、iPad、或iPod touch 上面的应用发起的。相反推送通知(又称远程通知)是从其他设备上面到达的。它来自一个远程设备——应用程序的提供者——并在有新的消息需要查看或新的数据需要下载的时候被推送到本地设备上面的应用,常见的本地通知像iphone的日历,微信或者qq这些都是本地推送,比如还安装了优酷,qq视频这些软件,允许推送后,每天会给你发些新的视频消息,这些就是远程推送。
苹果推送通知服务(APNs)是推送通知的网关,iPhone ipad 对于应用程序在后台运行有诸多限制,考虑到手机电池电量,应用不允许在后台进行过多的操作。因此,当用户切换到其他程序后,原先的程序无法保持运行状态。对于那些需要保持持续连接状态的应用程序(比如社区网络应用),将不能收到实时的信息。推送是解决轮询所造成的流量消耗和电量消耗的一个比较好的解决方案
为解决这一限制,苹果推出了APNs(苹果推送通知服务 Apple Push Notification services)。APNs 允许设备与苹果的推送通知服务器保持常连接状态。当你想发送一个推送通知给某个用户的iPhone上的应用程序时,你可以使用 APNs 发送一个推送消息给目标设备上已安装的某个应用程序。
苹果的推送服务APNs基本原理简单来说就是苹果利用自己专门的推送服务器(APNs)接收来自我们自己应用服务器的需要被推送的信息,然后推送到指定的iOS设备上,然后由设备通知到我们的应用程序,设备以通知或者声音的形式通知用户有新的消息。推送的前提是装有我们应用的设备需要向APNs服务器注册,注册成功后APNs服务器会返给我们一个device_token,拿到这个token后我们将这个token发给我们自己的应用服务器,当有需要被推送的消息时,我们的应用服务器会将消息按指定的格式打包,然后结合设备的device_token一并发给APNs服务器,由于我们的应用和APNs维持一个基于TCP的长连接,APNs将新消息推送到我们设备上,然后在屏幕上显示出新消息来。
顺便说下安卓的推送,Android消息推送采用MQTT协议,服务器端采用mosquito+PhpMQTTClient,有兴趣的同学可以看看
mosquito:http://mosquitto.org/
PhpMQTTClient :https://github.com/tokudu/PhpMQTTClient
整个过程基本就这样,下面我们看一下设备注册APNs的流程图:
上图完成了如下步骤:
1.Device连接APNs服务器并携带设备序列号
2.连接成功,APNs经过打包和处理产生device_token并返回给注册的Device
3.Device携带获取的device_token向我们自己的应用服务器注册
4.完成需要被推送的Device在APNs服务器和我们自己的应用服务器注册
执行顺序如下所示:
这里要提到的一点是,我们的设备和APNS服务器之间的通讯是基于SSL协议的TCP流通讯,二者之间维持一个长连接,当从APNS服务器注册成功后,一定要将device_token发送给我们的应用服务器,因为在推送过程中,首相是由我们的应用服务器(上图中Provider)将需要推送的消息结合device_token按指定格式(后面会提到)打包然后发送给APNS服务器,然后由APNS服务器推送给我们的设备。
好了,注册设备的过程完成了,接下来就是如何推送了:
推送的过程经过如下步骤:
1.首先,安装了具有推送功能的应用,我们的设备在有网络的情况下会连接苹果推送服务器,连接过程中,APNS会验证device_token,连接成功后维持一个长连接;
2.Provider(我们自己的服务器)收到需要被推送的消息并结合被推送设备的device_token一起打包发送给APNS服务器;
3.APNS服务器将推送信息推送给指定device_token的设备;
4.设备收到推送消息后通知我们的应用程序并显示和提示用户(声音、弹出框)
比较直观的流程参照下图:
上图显示了我们的应用服务器将消息推送到我们的App的完整路径,其实真正完成推送的是APNS服务器,我们自己的应用服务器只是将需要推送的消息告诉苹果服务器,至于如何维护消息队列或如何保证消息能被推送到指定的设备上,这些都由苹果APNS给我们做完了
四种:徽章、提示框、声音和横幅,具体表现形式如下图
Push机制的4个组件
Provider
APNS
iPhone设备
Client App
其中APNS(Apple Push Notification Service)是由苹果提供的消息推送服务中心,所有的消息都经由这里转发给相应的设备
你得有台ios设备,iphone,ipad
为了给提供者这边开发和配置推送通知,你必须从开发者中心(即苹果官网 DevCenter)取得 SSL 授权证书。每个授权证书限制必须对应一个单独的应用,由应用的Bundle ID 标识。而且该授权证书也被限制用于以下两个环境之一,沙箱环境(用于开发和测试)和生产环境。这些环境都拥有它自己的 IP 地址,而且需要它们自己的授权证书。你必须同时获得这些环境的的配置文件(即 Provisioning profiles)。
二进制接口是异步的,而且它使用 TCP 方式通过 sock 连接把二进制内容的推送通知发送给 APNs。沙箱和生产环境都有自己独立的接口,每个都有它自己的地址和端口。对于每个接口,你需要使用 TLS(或 SSL)和已经拿到的 SSL 授权证书来建立一条到 APNs 的安全通信通道。提供者把推送通知打包并通过该通道发送给 APNs。APNs 包含了一个反馈服务,它负责维护每个应用的传递通知失败的设备列表(即APNs 当前无法把推送通知传递到这些设备上面的对应的应用)。提供者应该周期性的连接到反馈服务来查看推送失败的设备以便它可以把之前失败的通知重复发送过
去。
开发状态服务器地址 gateway.sandbox.push.apple.com 2195
产品状态服务器地址 gateway.push.apple.com 2195
Development和Production两个版本对应的apns device token是不同的,前者是develop的mobileprovision下获取的。后者是production的mobileprovision获取的。
Development和Production两个版本可以共用一个App ID(不推荐。共用时每次调试前都要删除设备上的app,重新打包生成。而且公用appid会经常抓狂,早上行,下午就不行了。所以不推荐),但是不能共用一个mobileprovision,所以要单独生成Distribution的证书供production版本使用。
注:Distribution的版本是无法在设备上debug调试的!
Development和Production两个版本的code sign是不同的,前者是iPhone Developer,后者是iPhone Distribution。注意不能搞错。
无论是Development Push SSLCertificate还是Production Push SSL Certificate 都有过期时间的。Development Push SSL Certificate有效期大概四个月左右,而ProductionPush SSL Certificate的有效期是一年。需要注意在过期之前生成新的证书,以免影响使用。
先概述下大致过程,然后下面会截图给出详细的步骤
在Mac上生成 Apple推送通知SSL许可证:
1. 登录到 apple Developer Connection Portal 并点击 App IDs
2. 创建一个不使用通配符的 App ID 。通配符 ID 不能用于推送通知服务。例如,我们的iPhone程序ID像这样:54im.com.PushChat
3. 点击App ID旁的“Configure”,然后按下按钮生产 推送通知许可证。根据“向导”指导的步骤生成一个签名并上传,最后下载生成的许可证。
4. 通过双击.cer文件将你的 aps_developer_identity.cer 引入Keychain中。
5. 在Mac上启动 Keychain助手,然后在login keychain中选择 Certificates分类。你将看到一个可扩展选项“Apple Development Push Servicescom.54im.PushChat”
6. 扩展此选项然后右击“Apple Development Push Services” > Export “Apple Development Push Services:com.54im.PushChat”。保存为 PushChat_cert.p12 文件。
7. 扩展“Apple Development Push Services” 对“Private Key”做同样操作,保存为 PushChat_key.p12 文件。
8. 需要通过终端命令将这些文件转换为PEM格式:
openssl pkcs12 -clcerts -nokeys -out cert.pem -in PushChat_cert.p12
9. 转换得到key的pem:
openssl pkcs12 -nocerts -out key.pem -in PushChat_key.p12
10. 如果你想要移除密码,要么在导出/转换时不要设定或者执行:
openssl rsa -in key.pem -out key.unencrypted.pem
11. 最后,你需要将键和许可文件合成为apns-dev.pem文件,此文件在连接到APNS时需要使用:
cat apns-dev-cert.pem key.unencrypted.pem > ck.pem
首先登陆我们的Apple Developer后台为将要使用推送服务的App新建一个App ID,如下图,点击新建后输入基本信息
我把要改的地方截图下来了,高手勿笑哦,屌丝第一次用mac,也是第一次进苹果开发者后台。
APPID创建好后,我们点编辑刚刚生成好的APPID,生成下development证书,生产情况下用 Production证书
创建正式过程中,要求上传一张Certificate Signing Request 证书请求签名文件
这个请求文件在自己的mac上生成
输入证书信息
还记得刚刚苹果开发者那里要上传的证书不,将生成好的这个.certSigningRequest证书上传上去,
下载aps_development.cer这个证书到mac上,如果是发布版的推送证书,就为aps_production.cer。然后双击该证书,将推送证书安装到我们的Mac机器上,安装成功后会看到如下界面(如果是发布版,则证书的Development部分显示的是Production)
需要为certificate和它之下的private key各自export出一个.p12文件。(会出现设置密码过程)
导出私钥
需要将上面的2个.p12文件转成.pem格式:
openssl pkcs12 -clcerts -nokeys -out cert.pem -in Push_Chat_cert.p12
openssl pkcs12 -nocerts -out key.pem -in Push_Chat_cert_key.p12
如果需要对key不进行加密
openssl rsa -in key.pem -out key.unencrypted.pem
然后就可以 合并两个.pem文件, 这个ck.pem就是服务端需要的证书了
cat cert.pem key.unencrypted.pem > apns-dev.pem
创建 Provisioning Profile
接下来,需要创建 provisioning profile 以便允许应用程序安装到真实设备上。
将该prifiles文件下载下来,名字是PushChat.mobileprovision,其实不用下载,xcode里面会根据你的项目id自动去拉对于的这个文件。
在制作测试demo前我们先了解下开源的一个应用 easyapns,或者你也可以写demo,主要就是收集DeviceToken
啥是Easy APNS
Easy APNS 是一个用来管理苹果推送通知的PHP脚本。如果你对苹果推送通知后端部分比较感兴趣,而恰巧你有熟悉PHP,那么Easy APNS是你工具箱中必须的工 具。Easy APNS完全开源,并且设置非常简单。通过使用免费的、开源的PHP脚本,Easy APNS为开发者提供了一种很直观的可以用来控制整个 推送通知后端部分的方式。
项目官网:http://www.easyapns.com/
github地址:https://github.com/manifestinteractive/easyapns
github里面有这几个目录
delegate 将这里面代码添加到新建项目的 AppDelegate.m中
php 将文件放在一个可以访问的web目录下(要有php+mysql环境)
sql 将该目录sql导入到数据库
开始制作我们的客户端
在代码中加入以下代码
并且修改里面的push服务器地址
现在可以把项目编译到iphone或者ipad上面了,注意项目 General中team配置。
然后插上自己的iphone或者ipad,就可以点build按钮了,这时屏幕上会弹出一个警告框,是否允许消息推送,类似于
用户同于消息推送后,也可以自行再关闭掉,也可以自行调整通知方式
数据库导入
create database pushchat DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
mysql -h 127.0.0.1 -uroot -p –databse pushchat 修改apns.php 和samples.php配置文件 $db = new DbConnect(‘localhost’, ‘root’, ’54im.com’, ‘pushchat’); 然后单独修改class目录下class_APNS.php的证书和日志路径 private $DEVELOPMENT = ‘sandbox’ private $logPath = ‘/usr/local/apns/apns.log private $sandboxCertificate = ‘/usr/local/apns/ck.pem 当然你的证书也必须放到这个目录下(就是之前合并的那个证书) 在手机上打开我们安装的那个PushChat应用,然后数据库中就有手机发送过来的 消息发送测试,在服务器上执行samples.php 就可以 php -f samples.php 备注: 服务器端,如果是php的,那必须使用.pem的证书,如果是java的,那必须使用.p12的证书。(很可能还需要双击证书进行安装!) 额外的自定义值: IOS7之前,苹果对于一个设备上的多个APP,生成相同的DeviceToken。 以下通知负载的示例举例说明了表 3-1 所列举的属性。名为”acme”的属性是一个 记住,为了更好的性能,你应该在把字符串添加到通知里面之前尽可能的去除空 $ telnet gateway.sandbox.push.apple.com 2195 Trying 17.172.232.226… Connected to gateway.sandbox.push-apple.com.akadns.net. Escape character is ‘^]’. 它将尝试发送一个规则的,不加密的连接到APNS服务。如果你看到上面的反馈,那说明你的MAC能够到达APNS。按下Ctrl+C 关闭连接。如果得到一个错误信息,那么你需要确保你的防火墙允许2195端口。 然后再次连接,这次用我们的SSL证书和私钥来设置一个安全的连接: $ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem Enter pass phrase for PushChatKey.pem: 你会看到一个完整的输出,让你明白OpenSSL在后台做什么。如果连接是成功的,你可以键入一些字符。当你按下回车后,服务就会断开连接。如果在建立连接时有问题,OpenSSL将会给你一个错误消息,但是你不得不向上翻输出LOG,来找到它。 当然上面要测试prodution版本是否正确的话,把gateway.sandbox.push.apple.com换成gateway.push.apple.com就好。 上图显示的这个消息体就是我们的服务器(Provider)发送给APNS服务器的消息结构,APNS验证这个结构正确并提取其中的信息后,再将消息推送到指定的设备。这个结构体包括五个部分,第一个部分是命令标示符,第二个部分是我们的device_token的长度,第三部分是我们的device_token字符串,第四部分是推送消息体(Payload)的长度,最后一部分也就是真正的消息内容了,里面包含了推送消息的基本信息,比如消息内容,应用Icon右上角显示多少数字以及推送消息到达时所播放的声音等。接下来我们拆解看一下Payload(消息体)的结构: { 这其实就是个JSON结构体,alert标签的内容就是会显示在用户手机上的推送信息,badge显示的数量(注意是整型)是会在应用Icon右上角显示的数量,提示有多少条未读消息等,sound就是当推送信息送达是手机播放的声音,传defalut就标明使用系统默认声音,如果传比如“beep.wav”就会播放在我们应用工程目录下名称为beep.wav的音频文件,比如当手机锁屏时QQ在后台收到新消息时的滴滴声。 有这么一种情况,当我们将应用从设备卸载后,推送的消息改如何处理呢。我们知道,当我们将应用从设备卸载后,我们是收不到Provider给我们推送的消息的,但是,如何让APNS和Provider都知道不去向这台卸载了应用的设备推送消息呢?针对这个问题,苹果也已经帮我们解决了,那就是Feedback service。他是APNS的一部分,APNS会持续的更新Feedback service的列表,当我们的Provider将信息发给APNS推送到我们的设备时,如果这时设备无法将消息推送到指定的应用,就会向APNS服务器报告一个反馈信息,而这个信息就记录在feedback service中。按照这种方式,Provider应该定时的去检测Feedback service的列表,然后删除在自己数据库中记录的存在于反馈列表中的device_token,从而不再向这些设备发送推送信息。连接Feedback service的过程同样使用Socket的方式,连接上后,直接接收由APNS传输给我们的反馈列表,传输完成后断开连接,然后我们根据这个最新的反馈列表在更新我们自己的数据库,删除那些不再需要推送信息的设备的device_token。从Feedback service读取的数据结构如下: 结构中包含三个部分,第一部分是一个时间戳,记录的是设备失效后的时间信息,第二个部分是device_token的长度,第三部分就是失效的device_token,我们所要获取的就是第三部分,跟我们的数据库进行对比后,删除对应的device_token,下次不再向这些设备发送推送信息 a.消息大批量发送问题 目前由于APNS(Apple Push Notification Service)机制原因,目前easy apns的消息发送机制为: 对每一条发送的消息,为所有需要推送的设备都在数据库中apns_messages创建一条消息,然后通过轮训数据库表来一条一条向苹果消息推送服务器发送消息 在需要推送的设备较多的情况下,由于存在大量的网络链接,导致存在较长时间的延迟。 解决方案: 1、做批量消息推送时候,保持与苹果消息推送服务器的长链接 2、使用批量发送机制 You should also retain connections with APNs across multiple notifications. APNs may consider connections that are rapidly and repeatedly established and torn down as a denial-of-service attack. Upon error, APNs closes the connection on which the error occurred. As a provider, you are responsible for the following aspects of push notifications: You must compose the notification payload (see “The Notification Payload”). You are responsible for supplying the badge number to be displayed on the application icon. You should regularly connect with the feedback web server and fetch the current list of those devices that have repeatedly reported failed-delivery attempts. Then you should cease sending notifications to the devices associated with those applications. See “The Feedback Service” for more information. b、数据库轮询效率问题 由于目前easy apns是采用数据库轮询的方式来进行消息8. 测试
服务器端发出的json包是有大小限制的,最大256字节,包括自定义字典集。
aps中的alert字符串里是可以添加”\n”做换行的。
json包中除了alert,badge,sound之外,还是是可以自定值的。
$payload[‘aps’] = array(‘alert’ => ‘This is the alert text’, ‘badge’ => 1, ‘sound’ => ‘default’);
$payload[‘server’] = array(‘serverId’ => $serverId, ‘name’ => $name);
$output = json_encode($payload);
当用户按下“View”后,自定义server值将被传递到设备中的程序。JSON 值如下:
{
“aps” :
{ “alert” :
{
“action-loc-key” : “显示” ,
“body” : “This is the alert text”
},
“badge” : 1,
“sound” : “default” },
“server” : { “serverId” : 1, “name” : “Server name”)
}
256字节的限制适用于整个payload,包括自定义字典集。
IOS7以及之后,苹果对于一个设备上的多个APP,生成不同的DeviceToken。
这种新改变导致APNS上创建了一张新老token的映射表,如果你一直用老的token,那没问题,但是,一旦服务器使用新的DeviceToken,映射表中的记录就会被删除,这意味着,老的DeviceToken就不能用了,必然发送失败。
待验证:IOS5和IOS6,APP永远可以获取DeviceToken,除此之外的系统,如果用户拒绝了,或者关闭了推送,那么无法获取DeviceToken,走失败回调。8. 附录:
8.1 JSON示例
自定义负载数据的例子。该示例包含了可读性的空白符和换行符。为了提高性能,提
供者应该忽略空白符和换行符。
示例 1:以下负载包含哦一个简单的 aps 字典,采用默认的警告按钮的提示信息(关
闭按钮和查看按钮)。它使用字符串而不是字典作为 alert 的值。该负载同样包含了一
个自定义的属性数组。
{
“aps” : { “alert” : “Message received from Bob” },
“acme2″ : [ “bang”, “whiz” ]
}
示例 2:该示例的负载包含了一个 aps 的字典,指定设备显示一个警告消息并在左
边包含一个关闭按钮和右边显示一个本地化的”action”按钮。在该例中,”PLAY”被作
为键使用来从Localizable.strings文件里面当前偏好语言的字典里面获取对应的“Play”
的字符串。aps 字典同样要求应用程序的图标显示数字 5。
{
“aps” : {
“alert” : {
“body” : “Bob wants to play poker”,
“action-loc-key” : “PLAY”
},
“badge” : 5,
},
“acme1″ : “bar”,
“acme2″ : [ “bang”, “whiz” ]
}
2012-01-28 |? 2012 YouMi Mobile Co. Ltd. All Rights Reserved.
Local and Push Notification Programming Guide
示例 3:该示例的负载指定设备应用显示一个警告信息并包含关闭按钮和查看按
钮。同时它要求应用程序的图标显示数字 9,并在通知显示的时候播放主目录厦门的
bingbong.aiff 音频文件。
{
“aps” : {
“alert” : “You got your emails.”,
“badge” : 9,
“sound” : “bingbong.aiff”
},
“acme1″ : “bar”,
“acme2″ : 42
}
示例 4:该示例的负载主要关注的是使用 alert 字典里面的 loc-key 和 loc-args 子属
性来从应用程序的主目录下面获取一个本地化的字符串并根据 loc-args 选择合适的子
变量位于正确的位置。它同样指定了一个自定义声音文件 chime,并包含了一个自定
义属性。
{
“aps” : {
“alert” : { “loc-key” : “GAME_PLAY_REQUEST_FORMAT”, “loc-args” : [ “Jenna”,
“Frank”] },
“sound” : “chime”,
},
“acme” : “foo”
}
示例 5:下面的示例显示了一个空的 aps 字典,因为 badge 属性被隐藏了,所以当
前应用程序图标的任何数字都会被移除。而自定义属性 acme2 是一个包含两个整形的
数组。
{
“aps” : {
},
“acme2″ : [ 5, 8 ]
}
白字符和换行字符8.2 检验证书是否正确的方法:
8.3 Push故障排除
8.4 APNS信息包结构体
“aps” : {
“alert” : “You got your emails.”,
“badge” : 1,
“sound” : “default”
},
}8.5 消息大批量发送问题