APNS苹果远程推送

什么是APNS?

苹果推送通知服务(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将新消息推送到我们设备上,然后在屏幕上显示出新消息来。

推送流程

  • 获取设备device_token阶段

上图完成了如下步骤:

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服务器推送给我们的设备。

消息推送过程

推送的过程经过如下步骤:

19175942_bHsb.jpeg

1.首先,安装了具有推送功能的应用,我们的设备在有网络的情况下会连接苹果推送服务器,连接过程中,APNS会验证device_token,连接成功后维持一个长连接;

2.Provider(我们自己的服务器)收到需要被推送的消息并结合被推送设备的device_token一起打包发送给APNS服务器;

3.APNS服务器将推送信息推送给指定device_token的设备;

4.设备收到推送消息后通知我们的应用程序并显示和提示用户(声音、弹出框)

3.3 完整流程介绍

比较直观的流程参照下图:

![](http://upload-images.jianshu.io/upload_images/5220087-da89da5c9b8b43fb.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
应用启用推送通知功能,需要用户确认;

应用收到设备识别ID(device token),相当于接收推送通知的地址;

应用将设备识别ID发送到你开发的服务器;

当有推送通知的需要时,你就可以通过你开发的服务组件发送信息到苹果的服务器上;

苹果推送通知服务将信息推送到用户的设备上。

上图显示了我们的应用服务器将消息推送到我们的App的完整路径,其实真正完成推送的是APNS服务器,我们自己的应用服务器只是将需要推送的消息告诉苹果服务器,至于如何维护消息队列或如何保证消息能被推送到指定的设备上,这些都由苹果APNS给我们做完了

Push机制类型

四种:徽章、提示框、声音和横幅,具体表现形式如下图

  • Push机制的4个组件
  • Provider
  • APNS
  • iPhone设备
  • Client App
    其中APNS(Apple Push Notification Service)是由苹果提供的消息推送服务中心,所有的消息都经由这里转发给相应的设备

证书生成

![Uploading 19175943_bPmM_098454.jpeg . . .]

先概述下大致过程,然后下面会截图给出详细的步骤

在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

5.2.2 配置详解

  1. 创建APPID

首先登陆我们的Apple Developer后台为将要使用推送服务的App新建一个App ID,如下图,点击新建后输入基本信息

APPID创建好后,我们点编辑刚刚生成好的APPID,生成下development证书,生产情况下用 Production证书

创建正式过程中,要求上传一张Certificate Signing Request 证书请求签名文件

  1. 生成证书请求文件
    输入证书信息


3. 生成PUSH证书

还记得刚刚苹果开发者那里要上传的证书不,将生成好的这个.certSigningRequest证书上传上去,


下载aps_development.cer这个证书到mac上,如果是发布版的推送证书,就为aps_production.cer。然后双击该证书,将推送证书安装到我们的Mac机器上,安装成功后会看到如下界面(如果是发布版,则证书的Development部分显示的是Production)

需要为certificate和它之下的private key各自export出一个.p12文件。(会出现设置密码过程)

  1. 导出公钥


    19175956_NaCN.jpeg

导出私钥


19175957_UHfr.jpeg
  1. key转换

需要将上面的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

5.3 创建 provisioning profile

  1. key转换

需要将上面的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
5.3 创建 provisioning profile


![](http://upload-images.jianshu.io/upload_images/5220087-3f1d413cc7539c33.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
19180001_675W.jpeg

将该prifiles文件下载下来,名字是PushChat.mobileprovision,其实不用下载,xcode里面会根据你的项目id自动去拉对于的这个文件

客户端制作

#import "AppDelegate.h"
@implementation AppDelegate
/**
* This is what you need to add to your applicationDidFinishLaunching
*/
 - (void)applicationDidFinishLaunching:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
 [[UIApplication sharedApplication]

 registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];

// Clear application badge when app launches
application.applicationIconBadgeNumber = 0;
}
- (void)applicationDidFinishLaunching:(UIApplication *)application
 {
// Add registration for remote notifications

[[UIApplication sharedApplication]

 registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
 // Clear application badge when app launches
application.applicationIconBadgeNumber = 0;
}
/*
* -------------------------------------------------------
*  BEGIN APNS CODE
*/
/**
* Fetch and Format Device Token and Register Important Information to Remote Server
*/
 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
#if !TARGET_IPHONE_SIMULATOR
// Get Bundle Info for Remote Registration (handy if you have more than one app)

NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
// Check what Notifications the user has turned on.  We registered for all three, but they may have manually disabled some or all of them.

NSUInteger rntypes = [[UIApplication  sharedApplication] enabledRemoteNotificationTypes];
// Set the defaults to disabled unless we find otherwise...
NSString *pushBadge = (rntypes & UIRemoteNotificationTypeBadge) ? @"enabled" : @"disabled";

NSString *pushAlert = (rntypes & UIRemoteNotificationTypeAlert) ? @"enabled" : @"disabled";

NSString *pushSound = (rntypes & UIRemoteNotificationTypeSound) ? @"enabled" : @"disabled";
// Get the users Device Model, Display Name, Unique ID, Token & Version Number
UIDevice *dev = [UIDevice currentDevice];
NSString *deviceName = dev.name;
NSString *deviceModel = dev.model;
NSString *deviceSystemVersion = dev.systemVersion;
// Prepare the Device Token for Registration (remove spaces and < >)
NSString *deviceToken = [[[[devToken description]                              stringByReplacingOccurrencesOfString:@"<"withString:@""]                          stringByReplacingOccurrencesOfString:@">" withString:@""]                          stringByReplacingOccurrencesOfString: @" " withString: @""];
// Build URL String for Registration

// !!! CHANGE "www.mywebsite.com" TO YOUR WEBSITE. Leave out the http://

// !!! SAMPLE: "secure.awesomeapp.com"
NSString *host = @"121.199.25.24/pushchat";
// !!! CHANGE "/apns.php?" TO THE PATH TO WHERE apns.php IS INSTALLED
// !!! ( MUST START WITH / AND END WITH ? ).
// !!! SAMPLE: "/path/to/apns.php?"
NSString *urlString = [NSString stringWithFormat:@"/apns.php?task=%@&appname=%@&appversion=%@&devicetoken=%@&devicename=%@&devicemodel=%@&deviceversion=%@&pushbadge=%@&pushalert=%@&pushsound=%@", @"register", appName,appVersion, deviceToken, deviceName, deviceModel, deviceSystemVersion, pushBadge, pushAlert, pushSound];

// Register the Device Data

// !!! CHANGE "http" TO "https" IF YOU ARE USING HTTPS PROTOCOL

NSURL *url = [[NSURL alloc] initWithScheme:@"http" host:host path:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];

[NSURLConnection sendAsynchronousRequest:request

                                   queue:[NSOperationQueue mainQueue]

                       completionHandler:^(NSURLResponse *urlR, NSData *returnData, NSError *e) {

                           NSLog(@"Return Data: %@", returnData);

                       }];

NSLog(@"Register URL: %@", url);
#endif
}
/**
* Failed to Register for Remote Notifications
*/
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
#if !TARGET_IPHONE_SIMULATOR
 NSLog(@"Error in registration. Error: %@", error);
#endif
}
/**
* Remote Notification Received while application was open.
*/

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
#if !TARGET_IPHONE_SIMULATOR

NSLog(@"remote notification: %@",[userInfo description]);

NSDictionary *apsInfo = [userInfo objectForKey:@"aps"];

NSString *alert = [apsInfo objectForKey:@"alert"];

NSLog(@"Received Push Alert: %@", alert);
NSString *sound = [apsInfo objectForKey:@"sound"];

NSLog(@"Received Push Sound: %@", sound);
//AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
NSString *badge = [apsInfo objectForKey:@"badge"];
NSLog(@"Received Push Badge: %@", badge);
 application.applicationIconBadgeNumber = [[apsInfo objectForKey:@"badge"] integerValue];

#endif
}
/*
*  END APNS CODE
-------------------------------------------------------
 */
- (void)applicationWillResignActive:(UIApplication *)application
{

// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

并且修改里面的push服务器地址

NSString *host = @"api.54im.com/pushchat";

现在可以把项目编译到iphone或者ipad上面了,注意项目 General中team配置。


![ ![ ![](http://upload-images.jianshu.io/upload_images/5220087-dc3dc5068d2a89c9.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](http://upload-images.jianshu.io/upload_images/5220087-082f7287a6652996.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](http://upload-images.jianshu.io/upload_images/5220087-5f97f03508dee99a.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,737评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,103评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,710评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,909评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,794评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,557评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,939评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,572评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,852评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,871评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,692评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,490评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,939评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,114评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,409评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,971评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,176评论 2 339

推荐阅读更多精彩内容