推送通知iOS客户端编写实现及推送服务器端编写

APNS的推送机制

首先我们看一下苹果官方给出的对ios推送机制的解释。如下图

推送通知iOS客户端编写实现及推送服务器端编写


 

Provider就是我们自己程序的后台服务器,APNS是Apple Push Notification Service的缩写,也就是苹果的推送服务器。

上图可以分为三个阶段:

第一阶段:应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。 

第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。 

第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。

 

APNS推送通知的详细工作流程

下面这张图是说明APNS推送通知的详细工作流程:

推送通知iOS客户端编写实现及推送服务器端编写 

 

 

根据图片我们可以概括一下:

1、应用程序注册APNS消息推送。

2、iOS从APNS Server获取devicetoken,应用程序接收device token。

3、应用程序将device token发送给程序的PUSH服务端程序。

4、服务端程序向APNS服务发送消息。

5、APNS服务将消息发送给iPhone应用程序。

 

准备工作

首先要有一台苹果的设备,模拟器是不支持推送的,所以你需要一台iphone,ipod touch或者ipad。

 

我们的客户端与苹果服务器之间和我们自己的服务器与苹果服务器之间都需要证书来进行链接。下面我们来开始进入证书的制作过程。

 

一 CSR文件

首先我们要有生成一个Certificate Signing Request(也就是CSR)的请求文件。

在应用程序里的使用工具中找到钥匙串访问。

推送通知iOS客户端编写实现及推送服务器端编写

选择从证书颁发机构请求证书

推送通知iOS客户端编写实现及推送服务器端编写

填上你的邮箱和常用名,常用名要记一下,一会会用到。然后选择保存到磁盘,继续

推送通知iOS客户端编写实现及推送服务器端编写

保存位置在桌面,点击存储。

推送通知iOS客户端编写实现及推送服务器端编写

推送通知iOS客户端编写实现及推送服务器端编写

 

到这里点击完成后我们会在桌面上看到一个CertificateSigningRequest.certSigningRequest的请求文件,也就是我们说的CSR文件。在我们生成CSR文件的同时,会在钥匙串访问中生成一对秘钥,名称为刚才我们填写的常用名

 

二 下载开发证书和发布证书

(这里我为了大家能看清楚,已经把之前的证书事先吊销了)

https://developer.apple.com/devcenter/ios/index.action登录后,在右侧的ios Developer Program里点击iOS Provisioning Portal。

进入下一级页面后在左侧选择Certificates

 

配置Xcode工程

编写iOS推送应用需要在Xcode工程中进行一些配置,这些配置是主要是设置代码签名标识,代码签名标识的前提要有配置概要文件(Provisioning Profiles)。

有了配置概要文件即可以设置代码签名标识了,需要下载概要文件到本地,代码签名标识需要选择这个配置概要文件。选择TAGETS→MyNotes→Code Signing Identity,选择你自己的代码签名标识。

推送通知iOS客户端编写实现及推送服务器端编写

设置完成之后可以开始编码工作了。

代码实现

推送通知的代码实现主要分为两个步骤,第一步注册接收通知,第二步接收注册结果。这些工作都是在应用程序委托对象AppDelegate中实现的。

我们先看看注册部分的代码:

1 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2 {
3   //注册接收通知类型
4   [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
5 return YES; 6 }

注册过程比较长,它通过APNS从苹果公司返回,注册结束后的回调方法代码:

 1 - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
 2 {
 3       NSLog(@”设备令牌: %@”, deviceToken);
 4       NSString *tokeStr = [NSString stringWithFormat:@"%@",deviceToken];
 5       if ([tokeStr length] == 0) {
 6             return;
 7           }
 8     
 9       NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"\<\>"];
10       tokeStr = [tokeStr stringByTrimmingCharactersInSet:set];
11       tokeStr = [tokeStr stringByReplacingOccurrencesOfString:@" " withString:@""];
12       NSString *strURL = @”http://192.168.1.103/push_chat_service.php”;
13       NSURL *url = [NSURL URLWithString:strURL];
14       ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
15       [request setPostValue:tokeStr forKey:@"token"];
16       [request setPostValue:@"com.tryingx.gavin" forKey:@"appid" ];
17       [request setDelegate:self];
18       NSLog(@”发送给服务器”);
19       [request startAsynchronous];
20 }
21 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
22     
23     // 处理推送消息
24     NSLog(@"userinfo:%@",userInfo);
25     
26     NSLog(@"收到推送消息:%@",[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]);
27     //设置图标标记
28     application.applicationIconBadgeNumber = [[userInfo objectForKey:@"aps"] objectForKey:@"badge"];
29     
30 }
31 - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
32 {
33     NSLog(@”获得令牌失败: %@”, error);
34 }

2、推送服务端编程

内容提供者接收到设备的令牌并保存起来,在有新的内容需要推送的时候,他们将启动一个服务程序逐个设备推送他们的内容。在推送具体的过程中并非直接 由内容提供者,直接发送给用户设备,而是服务程序与APNS通讯建立信任连接,然后把数据推送给APNS,再由APNS利用安全通道推送给用户设备。

如果要编写内容提供者的推送服务程序,我们需要进行SSL认证编程,以及构建APNS数据包,数据包分为3个主要部分:Command(命令)、deviceToken(令牌)和Payload(载荷)。载荷不能超过256字节,是JSON格式,例如:

1 { 
2  “aps” : {
3         “alert” : ”You got your emails.”,
4         “badge” : 9,       
5             “sound” : ”bingbong.aiff”       
6           }
7 }                              

作为推出服务程序可以使用很多计算机语言实现,如果从便于管理角度看,使用PHP、Java和.NET,甚至是Note.js都是可以选择的。本书重点介绍PHP和Java编写推送服务程序。

使用PHP实现推送服务

PHP是非常不错的服务器端脚本,这么多年来没有被JavaEE和.NET蚕食掉,说明它有过人之处。PHP编程简单很多人原意使用,实现本章推送服务也很简单。下面代码是实现了推送的PHP代码:

 1 <?php
 2 
 3 // Put your device token here (without spaces):
 4 $deviceToken = '3761e118935ba9c74d55fc2913640dc483b1c293d5faa80eb189ce00bd25ea04';
 5 
 6 // Put your private key's passphrase here:密语
 7 $passphrase = 'tryingx';
 8 
 9 // Put your alert message here:
10 $message = '这是一条推送消息';
11 
12 ////////////////////////////////////////////////////////////////////////////////
13 
14 $ctx = stream_context_create();
15 stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
16 stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
17 
18 // Open a connection to the APNS server
19 $fp = stream_socket_client(
20     'ssl://gateway.push.apple.com:2195', $err,
21     $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
22 
23 if (!$fp)
24     exit("Failed to connect: $err $errstr" . PHP_EOL);
25 
26 echo 'Connected to APNS' . PHP_EOL;
27 
28 // Create the payload body
29 $body['aps'] = array(
30     'alert' => $message,
31     'sound' => 'default'
32     );
33 
34 // Encode the payload as JSON
35 $payload = json_encode($body);
36 
37 // Build the binary notification
38 $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
39 
40 // Send it to the server
41 $result = fwrite($fp, $msg, strlen($msg));
42 
43 if (!$result)
44     echo 'Message not delivered' . PHP_EOL;
45 else
46     echo 'Message successfully delivered' . PHP_EOL;
47 
48 // Close the connection to the server
49 fclose($fp);
50     
51 ?>

打开终端窗口执行如下命令:

$ openssl pkcs12 -in 证书.p12 -out apns-dev.pem -nodes 
Enter Import Password:
MAC verified OK

回车后需要输入密码,这个密码是导出“证书.p12”时候设置的密码。

PHP 代码编写完成可以运行PHP了,有两种方法可以运行,一种是把这个文件放到Apache HTTP服务器目录下,并保证Apache下安装和 PHP,然后用浏览器运行,在浏览器中输入http://localhost/phpPNs/Pusher.php,这个URL是我自己 Apache HTTP服务器上的PHP文件。

推送通知iOS客户端编写实现及推送服务器端编写

另外一种方法,简单的多,我们不需要安装Apache HTTP服务器,只需要安装了PHP解释器就可以了,我们在终端中运行下面的指令:

$ php -f Pusher.php

连接OK

发送消息:{“aps”:{“alert”:”\u65b0\u5e74\u597d. from PHP”,”badge”:11,”sound”:”default”}}

这样就可以推送通知了,如果一切正常推送成功,用户的设备就可以接收到通知了。

使用Java推送服务

上 面我们介绍了PHP实现的推送服务程序,使用其它语言编写基本的过程也一样的,这一节我们就介绍Java推送服务程序。由于具体的流程与PHP一 样,这里我们就不再自己编写Java的实现代码,而是使用别人已经封装好的javapns(http://code.google.com/p /javapns/)类库,它封装了socket实现细节问题,开发起来变的比较简单了。

下面代码是实现了推送的Java代码:

 1 package com.tryingx;
 2 
 3 import javapns.Push;
 4 import javapns.notification.PushNotificationPayload;
 5 
 6 public class Pusher {
 7   public static void main(String[] args) {
 8   try {
 9     PushNotificationPayload payload = new PushNotificationPayload(); 
10     payload.addCustomAlertBody(“新年好!from Java”); 
11     payload.addBadge(11);  
12     payload.addSound(“default”); 
13     Push.payload(payload, ”ssl/证书.p12″, ”51work6.com”, false,
14     “1634899aef6c71ed5c0667d6658677a914c5ec3b545887e8173854970dee24db”); 
15     } catch (Exception e) {
16       e.printStackTrace();
17       }
18     }
19   }
20 }

上面的代码还依赖与下面的类库:bcprov-jdk15-146.jar、JavaPNS_2.2.jar和log4j-1.2.15.jar。 其中bcprov-jdk15-146.jar和JavaPNS_2.2.jar可以在http://code.google.com/p /javapns/下载到。log4j-1.2.15.jar到http://logging.apache.org/log4j/1.2 /download.html下载。

这个Java程序的运行我们就不再介绍了,它是一个Java Application可以在有jre环境运行,我们可以把它做成JavaWeb程序,这样就可以在浏览器中发送通知了。