iOS推送机制详解

简介

APNs 是 Apple Push Notification service 的简称 苹果推送通知服务
为什么会有 APNs ?
由于移动设备内存、CPU、电量的局限性,iOS 不允许 APP 的进程常驻后台(事实上可以申请后台运行一段时间,最长约10分钟)。
当用户主动杀掉APP或者APP进入后台超过限定时长时,就意味着该APP进程的结束。这很大程度保障了前台APP的流畅性,也延长了手机的使用时长。这也是苹果用户体验好的原因之一。但是这也意味着,服务器无法主动和用户交互(如推送实时消息等)。为了解决这个限制,苹果推出了APNs,允许设备和服务器分别与苹果的推送通知服务器保持长连接状态。
假如我们的苹果设备(AC电脑、笔记本、iPad、iPhone等)安装了一个名叫XXX的应用。那么我们具体是怎样接收到推送通知的?流程如下图:


iOS推送机制详解_第1张图片
image.png

安全结构

APNs 采用双层信任机制:连接信任 和 device token(设备令牌) 信任

连接信任:服务器(Provider)与 APNs 之间 | APNs 与设备之间

服务器与 APNs :有两种方式建立连接信任

1.基于令牌(JWT: Json Web Tokens)的连接信任

Provider只需要提供一对公私钥(私钥Provider自己保存,公钥苹果保存),并使用其中的私钥生成并加密JWT Token,每次向APNs请求推送的时候带上这个Token即可。
iOS推送机制详解_第2张图片
image.png
  • Provider通过TLS向APNs发起请求。
  • APNs返回一个证书给Provider。
  • Provier验证这个证书。通过后,发送push数据并带上JWT token。
  • APNs验证token,并返回请求的结果。
TLS:为了保证数据传输安全,在HTTP层与TCP层之间插入的一个安全校验层

基于令牌的连接方式需要注意:应该定时更新token,token的有效期为1小时。

  • 对于基于JWT的方式,能每次请求都创建新的token,尽量在一小时内使用同一个token。
  • 不能频繁的建立、关闭连接,否则APNs会把这当做是黑客攻击,拒绝访问。应该尽量将连接保活,直到你认为这个连接接下来会长时间不使用为止。
  • 当需要发送大量的推送数据的时候,可以同时创建多个连接,以改善性能。
  • 当吊销证书或者token时,应该关闭所有相关的连接。

2.基于证书的连接信任

这种方式是指Provider可以采用一个唯一的证书以及一个加密的私钥来与APNs交互,其中证书是由苹果产生的(通过苹果账号登录到developer account配置创建)。

iOS推送机制详解_第3张图片
image.png

  • Provider通过TLS向APNs请求连接。
  • APNs向Provider返回一个APNs证书。
  • Provider需要把你之前从开发者帐户中生成的证书发给 APNs
  • APNs 验证服务器提供的证书是否正确,如果正确,则确定服务器与 APNs 的信任连接。此时服务器可以向 APNs 发送推送请求了。
APNs 与每个设备之间的连接是自动建立的,这个过程中,不需要你的 app 做什么。

每个设备都有一个加密的证书和私有密钥,在设备激活的时候生成,并保存在设备的 keychain 中,在设备激活的时候,APNs 根据设备的证书和密钥验证并授权与设备之间的连接。
iOS推送机制详解_第4张图片
image.png
  • 当设备启动与 APNs 的 TLS 连接时,信任协商开始(图中顶部的箭头)。
  • APNs 将 APNs证书 返回给设备。
  • 操作系统验证此证书后,它会向 APNs 发送设备证书(图“设备证书”中的箭头)。
  • 最后,APNs 验证设备证书并建立信任(图中底部的箭头)。

设备token的生成与分发

什么是deviceToken ?
deviceToken是一个APP装在一个设备上的唯一标识符。一个APP在不同的设备上deviceToken不一样;一个APP删除之后再次安装deviceToken也不一样。

在项目代码AppDelegate里面有一个回调方法,当APNs注册成功通过该回调方法可以获取到返回的deviceToken

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

上面说过当有消息需要被推送时,我们自己的服务器会将消息按指定的格式结合设备的deviceToken一并打包 ,APNs拿到这个包之后验证这个包结构是否正确并提取其中的信息后,再将消息推送到指定的设备。

在app启动的时候,必须向iOS系统注册远程推送,成功后,苹果将会返回一个设备token给app,此时app就可以将这个token上报给自己的后台。

如果有必要产生一个新的token,APNs会使用设备证书生成一个token(其中包含了一个设备ID),并使用token key加密后返回给设备。同时设备会将这个token以NSData对象的形式返回给app,app获取到该token之后应该将其发送到自己后台,后台之后就可以通过这个token来发送推送数据。过程如下图:
iOS推送机制详解_第5张图片
image.png
  • 设备自动将用户的UDID 和应用的 AppID发送到APNs,APNs返回一个deviceToken给iOS设备
  • 设备将deviceToken发送到自己公司的后台服务器,用以保存;
  • 当后台需要推送消息给用户时,服务器将需要推送的消息和deviceToken传输给APNs;
  • APNs将消息推送给指定的deviceToken对应的手机;
  • 用户接收消息;

接下来就来看看这个包的结构:

image

这个包分五个部分

  • 第一个部分是命令标示符
  • 第二个部分是我们的deviceToken的长度
  • 第三个部分是我们的deviceToken字符串
  • 第四个部分是推送的消息体(Payload)的长度
  • 最后一个部分也就是真正的消息内容了,里面包含了推送消息的基本信息。(比如消息内容,应用icon右上角显示的数字角标以及推送消息到达时所播放的声音等)

当应用从设备卸载后,推送的消息改如何处理??

我们知道当应用从设备卸载后,是收不到推送消息的。但是,如何让APNs和Provider知道不再向这台卸载了应用的设备推送消息呢?

Feedback Service。它是APNS的一部分,APNs会持续的更新Feedback service的列表,当我们的Provider将信息发给APNs服务器,APNs推送信息到我们的设备时,如果这时设备无法将消息推送到指定的应用(应用已经删除),就会向APNs服务器发送一个反馈信息,而这个信息就记录在Feedback service中。按照这种方式,Provider应该定时的去检测Feedback service的列表,然后删除在自己数据库中记录的存在于反馈列表中的deviceToken,从而不再向这些设备发送推送信息。连接Feedback service的过程同样使用长连接的方式,连接上后,直接接收由APNs传输给我们的反馈列表,传输完成后断开连接,然后我们根据这个最新的反馈列表在更新我们自己的数据库,删除那些不再需要推送信息的设备的deviceToken。

从Feedback service读取的数据结构如下:

image

结构中包含三个部分

  • 第一部分是一个时间戳,记录的是设备失效后的时间信息
  • 第二个部分是deviceToken的长度
  • 第三部分就是失效的deviceToken,我们所要获取的就是第三部分,跟我们的数据库进行对比后,删除对应的deviceToken,下次不再向这些设备发送推送信息

deviceToken注意:不是设备唯一标识,是设备和应用组合的唯一标识。同一个手机不同的应用会生成不同的deviceToken,同一个手机同一个应用只生成一个deviceToken。在iOS7.0和iOS8.0的系统中,卸载重装应用,deviceToken不会发生变化,但是iOS9.0(包括)以后卸载重装应用,deviceToken会发生变化。

服务器的职责

服务器在跟 APNs 沟通连接的时候具有以下责任:

  • 通过 APNs 接收并管理关于你的app的全球唯一的设备验证码,及其它一些数据
  • 根据app功能的需要,决定通知推送的时间
  • 建立通知请求,并发送通知请求到 APNs, APNs 再将通知递送到相应的设备

推送内容:

远程推送通知,分为 普通推送 / 后台推送 / 静默推送 3 种类型

  • 普通推送
    • 就是我们在手机上平时见到的推送通知。
    • 包含声音、横幅、角标、自定义字段。
    • 处于前台,不会展示横幅,可通过 didReceiveRemoteNotification(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:(iOS 7 after)获取通知内容(前台展示横幅的方法看这里)。
    • 处于后台,会展示横幅,无法获取通知内容。
    • 处于退出,会展示横幅,无法获取通知内容。
    • 点击图标启动,无法获取通知内容。
    • 点击通知横幅启动,在 didFinishLaunchingWithOptions 获取通知内容。
{
 aps =     {
   alert = "显示内容";
   badge = 1;  // App 角标, 1)当badge>0时,角标会随badge而变化。2)当badge=0时,角标维持不变。3)当badge<0时,角标维持不变。
   sound = default;  // 推送声音,默认系统三全音,如需使用自己的声音,需要将声音文件拖拽&拷贝至 Xcode 工程目录任意位置,并在推送时指定其文件名
 };
 key1 = value1;  // 自定义字段,可设置多组,用于处理内部逻辑
 key2 = value2;
}
  • 后台推送
    • 各种显示效果跟普通推送完全一样。
    • 必须携带 "content-available" = 1;
    • 必须携带 alert、badge、sound 中 至少 1 个字段。
    • 仅 iOS 7 以后支持。
    • 必须在 Xcode 工程中 TARGETS – Capabilities – Background Modes – Remote notifications 开启该功能,具体可参照 iOS 7 Background Remote Notification。
    • 处于前台,可通过didReceiveRemoteNotification(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:(iOS 7 after) 获取通知内容。
    • 处于后台,可通过 didReceiveRemoteNotification:fetchCompletion Handler: 获取通知内容 // 获取情况中与普通推送的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作。
    • 处于退出,无法获取通知内容。
    • 点击图标启动,无法获取通知内容。
    • 点击推送横幅启动,在 didFinishLaunchingWithOptions 获取通知内容。
    {
      aps =     {
        alert = "显示内容";
        badge = 1;
        "content-available" = 1;  // 必带字段
        sound = default;
      };
      key1 = value1;
    }
    
  • 静默推送
    • 没有任何展示效果。
    • 必须携带 "content-available" = 1;,因此静默必然是后台的。
    • 必须不携带 alert、badge、sound。
    • 可携带自定义字段。
    • 处于前台,可通过didReceiveRemoteNotification(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:(iOS 7 after) 获取通知内容。
    • 处于后台,可通过 didReceiveRemoteNotification:fetchCompletion Handler: 获取通知内容 // 获取情况中与普通推送的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作。
    • 处于退出,无法获取通知内容。
{
    aps =     {
    alert = "";
    "content-available" = 1;  // 必带字段
    };
    key1 = value1;
}

新旧协议:基于二进制的旧 APNs 协议、基于 HTTP/2 的新 APNs 协议:

新旧协议的对比:

旧协议 新协议
连接不稳定 长连接,提供检测是否可用的方法
发送给APNs成功没有相应 成功失败均有相应
最大支持256字节 最大4k
- Request 和 Response 支持JSON网络协议
不同的推送,需要配置不同的推送证书 不同推送类型,只需要一种推送证书 Universal Push Notification Client SSL 证书
iOS推送机制详解_第6张图片
image.png

iOS推送机制详解_第7张图片
image.png

APNs基于 HTTP/2 的新 APNs 协议服务推出时间:
iOS推送机制详解_第8张图片
image.png

新 APNs 协议仍然存在的问题:不能保证推送肯定到达手机。当 APNs 向你发送了多条推送,但是你的设备网络状况不好,在 APNs 那里下线了,这时 APNs 到你的手机的链路上有多条任务堆积,APNs 的处理方式是,只保留最后一条消息推送给你,然后告知你推送数。那么其他三条消息呢?会被APNs丢弃。

你可能感兴趣的:(iOS推送机制详解)