Push的基本原理

可以用苹果官方文档中的一张图片简单的概括:

Push的流程

它是基于这样一种工作流程:

  • 我们的iOS设备,只要处于联网状态,就会和苹果的服务器保持长连接,以便苹果的各种消息服务能够正常工作(比如说iMassage、FaceTime、以及我们要用到的APNs服务等) 。
  • 当用户装了我们实现Push功能的应用,启动应用时会向苹果注册DeviceToken (这个token是消息推送的唯一标识,APNs寻找消息推送的设备就是靠这个标识,但是这个token不可以作为设备的唯一标识使用,因为当用户重装设备系统或者恢复设备的时候DeviceToken会发生改变),我们获得这个DeviceToken之后需要发送给我们的服务器(即Provider)。
  • 当有推送需求的时候,Provider把消息和DeviceToken按照指定的格式发送给APNs。
  • APNs收到Provider发送的消息,会根据DeviceToken查找对应的设备,如果设备在线,则会把消息发送给设备,如果设备不在线,则会等到设备在线的时候把消息推送到设备。
    注:苹果并不保证每条消息都会推送成功。
  • 设备收到消息之后,根据SSL证书判断是哪个程序,然后程序再做相应的处理(比如显示消息文字、声音、消息数量等)。

流程了解完毕,下面就可以考虑实现了。实现其实很简单,只需要生成所需要的证书,然后简单的设置一下,写几行代码就可以搞定。

生成各种需要的证书

相信程序做到推送这里的人肯定都会把CSR文件、APP ID 以及真机调试文件之类的安装好了,我们需要做的就只是在开发者中心的APP ID配置中选择对应的APP ID,然后点击Configure,打开Enable for Apple Push Notification Service选项,该选项下有Development和Production两个SSL证书可以配置,显然其分别为开发证书和产品证书。我们测试的话就生成Development证书就可以了。根据CSR文件,生成相应的SSL Certificate,然后保存,假如命名为aps_development.cer。然后我们需要单击以下aps_development以安装到我们的MAC上。

然后我们在钥匙串中导出aps_development.cer为**.p12**,设置密码,选择保存路径,假设命名为aps_development_key.p12(如果你的证书是在别的Mac上生成的,则导出为.p12这个选项可能是灰色的,不能点击,这时你需要在原电脑上导出.p12)。

下面我们要用生成的证书,生成服务器端所需要的证书。

此时应该有3个证书:

  1. CertificateSigningRequest.certSigningRequest
  2. aps_development.cer
  3. aps_development_key.p12
  • 将aps_development.cer转换成pem格式
openssl x509 -in aps_development.cer -inform DER -out aps_development.pem -outform PEM
  • 将aps_development_key.p12转换成aps_development_key.pem
openssl pkcs12 -nocerts -out aps_development_key.pem -in aps_development_key.p12
  • 创建用于服务器端的SSL p12证书aps_development_service.p12
openssl pkcs12 -export -in aps_development.pem -inkey aps_development_key.pem -certfile CertificateSigningRequest.certSigningRequest -name "aps_development_service" -out aps_development_service.p12

这个步骤需要输入3次左右的密码,建议密码和aps_development_key.p12的密码一致,以防混淆。

这里生成的aps_development_service.p12就是服务器向APNs服务器发送Push消息需要的证书。把证书和密码给服务器端,他们就可以连接APNs并发送消息了。

苹果APNs服务器有两个地址,分别为测试的沙盒地址:
gateway.sandbox.push.apple.com:2195
以及正式地址:
gateway.push.apple.com:2195

客户端获取DeviceToken

  1. 注册Push服务
    服务器想要给指定设备发送消息,需要知道这个设备的DeviceToken,然后上传到服务器。下面就来说明如何获得DeviceToken。
    想要能够接收到苹果返回的DeviceToken,需要先注册Push功能:(iOS8和之前注册方式不同,请注意判断)
    //推送注册
    if (iOS8) {
        [[UIApplication sharedApplication] registerForRemoteNotifications];
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil]];
    }else{
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge)];
    }
  1. 接收token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    //获取token
    NSString *token = [NSString stringWithFormat:@"%@",deviceToken];
    NSLog(@"获取asseccToken%@",token);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    NSLog(@"获取asseccToken失败%@",error);
}

拿到了这个DeviceToken,就可以上传到服务器了,服务器就可以用这个DeviceToken给设备发消息了。

服务器发送Push消息

苹果对于所发的消息也有严格的要求,必须要按照苹果的格式,且内容不能超过256字节,如果超过,APNs则不会推送给设备。

基本格式:

{
  "aps" : {
    "alert" : {
      "title" : "我是标题",
      "body" : "我是内容"
    },
    "sound" : "default",
    "badge" : 999
  }
}

其中aps键值是必须的,badge表示应用图标小红点所显示的数字,为0则不显示。sound表示提示音。

如果需要添加自定义数据,请加到aps空间之外。

{
  "aps" : {
    "alert" : {
      "title" : "我是标题",
      "body" : "我是内容"
    },
    "sound" : "default",
    "badge" : 999
  }
  "page":"home"
}

为了减小消息体积,可以把page写成p,home写成1、2、3之类的,和服务器人员统一好结构。比如"p":"1"。

详细结构参考苹果官方文档:ApplePushService

客户端接收Push

服务器发送消息后,经过APNs的处理,就可以到达我们的设备了。我们需要用下面的代码接收消息。

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    NSLog(@"%@",userInfo);
}

如果应用还没启动,则可以在下面的方法中获取消息。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //推送信息
    NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
}

如果服务器给badge设置了值的话,应用图标会一直显示数字,可以用下面的函数清空。

[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];

如果你做完了这些,迫切的想测试结果,但是服务器那边还没开发完成的话,你可以用下面的工具来测试。
Push测试工具

因为我们公司的服务器是用C#写的,所以附上一个可用的项目地址吧:
C#服务器端代码

小结

只要把各种证书和Token设置好,苹果的推送相对来说特别的简单。
苹果把推送完全控制在自己手里,对开发者和用户来说,都有很大的好处。对于开发者来说,用官方的接口能够很方便的集成推送功能,对用户来说,不会每天都收到乱七八糟的各种推送。这种为用户和开发者考虑的行为,真的很让人舒服。