本人第一次写博客,写的不好的地方大家请见谅。
本文主要参考了,感谢showhilllee详细的讲解。因为在做的过程中有些自己的理解和变化,所以自己再写一遍关于推送的详细博客。
本文分为四部分介绍:
一、解释APNS远程推送
二、配置推送的证书
三、导出自己服务器可用的证书
四、代码实现设备注册推送、获取推送消息
好了,现在开始正式讲解推送。
一、首先上一个老图,苹果很早就给大家的,推送流程图。
虽然ios更新很多版本,但是推送的这个流程从没有变过。推送的机制是:设备应用中首次打开向APNS(苹果推送服务器)注册推送,并获取设备令牌deviceToken->应用内将deviceToken发送到自己的推送服务器或第三方推送服务器进行保存->自己的服务器或第三方推送服务器将消息和deviceToken捆绑发送到APNS->APNS发送消息到设备,设备显示推送消息。
deviceToken类似UUID,在应用第一次请求deviceToken后,如果应用没有删除重新安装deviceToken是不会变的,应用删除后再重新安装,再注册远程推送后deviceToken会变化。所以每次应用打开都应该去注册推送并请求deviceToken,第一次会有系统提示是否推送,选择推送后,会收到回调函数中deviceToken,第一次会有些延迟,以后再开应用请求deviceToken就秒回了。
苹果的APNS服务器就是专门做推送的,分为测试地址和正式地址,所有苹果的推送消息都要经过APNS推送发到设备,无论测试还是正式。其实推送的原理就是设备将获得的deviceToken发送给自己的服务器保存,自己的服务器选择保存的deviceToken和要推送的消息打包发给APNS,APNS自动推送到指定的设备上。第三方推送在苹果推送这方面只是在自己的服务器和APNS中间加了一步,自己的服务器将消息和设备标识发给第三方服务器,第三方推送再将设备表示找到deviceToken,将消息发送到APNS。
二、配置推送的证书
1.打开本地钥匙串,左上角钥匙串访问-》证书助理-》从证书颁发机构请求证书
用户电子邮件地址一般就填开发者账号邮箱,选择存储到磁盘,保存在桌面,名字不用改。
2、(1)打开浏览器,登录apple developer,选择顶部导航栏Account,登录
进入界面后,选择Certificates-Development 点击右上角加号
选择Development的第二个选项,苹果开发服务证书Apple Push Notification service SSL(APNs),点击continue
这里需要选择一个App ID,我们先不讲从哪生成,假设已经有了一个了,点击下一步continue
点击choose file 选择本机生成的
点击continue,下载生成的aps_development.cer文件,并保存在桌面新建一个push的文件夹。
(2)下面说下App ID新建,点击Identifiers->App IDs->右上角加号
进入此界面后,name就设为你项目的名字吧(随意),Bundle ID要与项目中Bundle ID一致,选择Explicit AppID是为了唯一证书,Wildcard App ID是团队证书,做推送不用Wildcard App ID,要选择Explicit AppID。
在此页面下面勾上Push Notifications
点击continues后在此界面点击register就好了
接下来就是在(1)部分中填写App ID的步骤了。
(3)下载.mobileprovision文件 点击Provisioning Profiles-》All-》右上角加号。
因为是开发环境,所以选择Development的ios app,发布环境就选择Distribution。接下来选择AppID,Cer,Devices,也就是“哪个应用,哪个证书,哪个设备”的逻辑。
Profile Name可以随便起。下载.mobileprovision文件并放在刚才新建的push文件夹。
好了,至此,证书都配置好了。
三、配置.p12文件和windows服务器所需的.pem文件
.p12文件是服务器推送需要的验证证书,如果用第三方推送则需要这个文件,先讲怎么生成这个文件
1、首先双击安装上一节生成的.cer和.mobileprovision文件,安装。
打开钥匙串,找到刚安装的.cer证书,点击-》右键-》导出"*****",弹出这个窗口
名字随便起,为防止意外不要用中文了,保存到刚才新建的push文件夹,点击存储后弹出这个窗口
这是.p12文件需要设置一个访问密码,输入密码,简单点就123456了,验证这再输入一遍刚才输入的123456,点击好。
如果用第三方推送那导出.p12文件就够了。
2、如果自己搭建推送服务器还是windows的就需要.pem文件了,下面我们将.cer文件和刚生成的.p12文件分别转换成.pem文件。
打开终端,敲入cd 并把push文件夹的路径输入,回车,进入push文件夹
可以敲完cd空格后,在finder中找到push文件夹,按住画圈的push那蓝色的文件夹图标拖入终端,直接显示路径。
(1)进入push文件夹后输入指令:openssl x509 -in aps_development.cer -inform der -out PushChatCert1.pem
这个指令的意思是将下载的aps_development.cer证书文件生成名叫PushChatCert1的.pem文件,这个.pem文件名随便起,但你要标示出这个是.cer文件转换生成的。
(2)再输入指令:openssl pkcs12 -nocerts -out PushChatKey.pem -in SharePush.p12,输入p12密码并设置pem密码,为了方便设置成一样的。 这个指令是将.p12文件生成.pem文件,PusheChatKey.pem的名字随便起,同上,也要知道这个是.p12文件生成的。
输入完指令后需要输入.p12的密码,回车后需要设置这个.pem文件的密码,为了方便设置和.p12文件一样的密码123456
(3)将刚才生成的两个.pem文件合成一个.pem文件,指令:cat PushChatCert1.pem PushChatKey.pem > ck.pem
合成新的.pem文件名叫ck,这个随便起,自己记住就行。
(4)接下来测试一下这两个文件连接苹果测试推送服务器
输入测试连接指令:telnet gateway.sandbox.push.apple.com 2195。control+c断连接
输入测试通知指令:通知openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert1.pem -key PushChatKey.pem 。输入任何字符切断连接
ok,至此环境和证书都配置好了。下面开始敲代码。
四、打开xcode,新建一个项目,这个就不说了,打开Appdelegate.m文件
(1)在
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions函数中输入下面代码来想APNs注册推送,第一行代码的意思是当应用打开后将应用图标右上角消息数置空。
后面判断设备系统版本的原因是自iOS8.0后推送通知的函数发生了变化。
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
if ([[[UIDevice currentDevice] systemVersion] floatValue]>=8.0) {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}else{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeAlert];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
//这里处理deviceToken,将获取的deviceToken保存自己的服务器或者与别的信息一起捆绑
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
// NSLog(@"userinfo:%@",userInfo);
// NSLog(@"收到推送消息:%@",[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]);
NSString *message = [NSString stringWithFormat:@" 新消息:%@",[[userInfo objectForKey:@"aps"]objectForKey:@"alert"]];
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSString *str = [NSString stringWithFormat: @"Error: %@", err];
NSLog(@"获取令牌失败: %@",str);
}
didRegisterForRemoteNotificationsWithDeviceToken这个函数的作用是注册成功后返回devicetoken。
didReceiveRemoteNotification这个函数的作用是获取到推送消息回调的,参数userInfo是消息体,类型是字典型,通过[[userInfo objectForKey:@"aps"]objectForKey:@"alert"]可取到具体的消息内容。
didFailToRegisterForRemoteNotificationsWithError这个函数的作用是如果注册推送失败了回调,你可以做一些处理。
这样项目中也写好了推送的代码,下面简单写了一个php文件,当服务器给苹果推送发一条推送,devicetoken就用刚才项目执行后收到的devicetoken。
五、php代码
<?php
// ??????????deviceToken???????????????
$deviceToken = '这里写获取到的devicetoken';
// Put your private key's passphrase here:
$passphrase = '这里写之前设置的pem文件的密码';
// Put your alert message here:
$message = '推送测试:hellow world!';
////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '这里写之前把两个pem文件合成的一个pem文件名');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
//??????????
//$fp = stream_socket_client(?ssl://gateway.push.apple.com:2195?, $err, $errstr, 60, //STREAM_CLIENT_CONNECT, $ctx);
//?????????????appstore??????
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'badge' => 1,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
?>
保存php文件在之前说的push文件夹,名字就叫push.php。
打开终端,进入push文件夹,输入指令:php push.php,回车执行。
好了,终于写完了,第一次写博客太费劲了,之前保存草稿后再打开草稿没有图片了,图还得重新截,还有写了一半就浏览器'死了',还是重新写。。。额,就想了个办法,先把所有图截好,再写博客,这样还好。总之写完了,写的不好的地方大家轻喷啊~