本人第一次写博客,写的不好的地方大家请见谅。

   本文主要参考了,感谢showhilllee详细的讲解。因为在做的过程中有些自己的理解和变化,所以自己再写一遍关于推送的详细博客。

   本文分为四部分介绍:

一、解释APNS远程推送

二、配置推送的证书

三、导出自己服务器可用的证书

四、代码实现设备注册推送、获取推送消息

好了,现在开始正式讲解推送。

 

一、首先上一个老图,苹果很早就给大家的,推送流程图。

ios 创建推送p12 ios推送流程_移动开发

   虽然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,回车执行。

 

好了,终于写完了,第一次写博客太费劲了,之前保存草稿后再打开草稿没有图片了,图还得重新截,还有写了一半就浏览器'死了',还是重新写。。。额,就想了个办法,先把所有图截好,再写博客,这样还好。总之写完了,写的不好的地方大家轻喷啊~