zoukankan      html  css  js  c++  java
  • 消息推送之APNS

    利用APNS进行消息推送

    原理

    APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器。

    APNS推送可以分为三个阶段:

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

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

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

    详细流程如下:

    1、首先是应用程序注册消息推送服务。

    2、APNS向应用程序返回deviceToken。

    3、应用程序将deviceToken发送给推送服务端程序。

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

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

     

    证书生成

    网上有很多关于证书生成的详细步骤,这里不再说明了。

    最终生成的证书共包含下面四个

    1、pushNotification.certSigningRequest

    2、aps_development.cer(下载生成的支持推送服务的证书。)

    3、pushNotificationDevprofile.mobileprovision

    4、pushNotification.p12

     

    下面直接上代码。

     

    客户端

    1、应用程序注册消息推送服务

    在AppDelegate.m的(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中加入注册消息通知推送服务。

    1 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    2 {
    3   //判断是否由远程消息通知触发应用程序启动
    4     if ([launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]!=nil) {
    5         NSLog(@"远程消息通知触发应用程序启动");
    6     }
    7     //消息推送注册
    8     [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeBadge];
    9 }

    2、接收deviceToken的方法

    在项目的AppDelegate.m中加入以下两个代理方法

    1 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 
    2     NSString *token = [NSString stringWithFormat:@"%@", deviceToken]; 
    3     //获取终端设备标识,标识获取后需要将其发送到服务器端,服务器端推送消息到APNS时需要知道终端的标识,APNS通过注册的终端标识找到终端设备。
    4     NSLog(@"My token is:%@", token);   
    5 }  
    6 - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {   
    7     NSString *error_str = [NSString stringWithFormat: @"%@", error];   
    8     NSLog(@"Failed to get token, error:%@", error_str);   
    9 } 

    3、接收消息的处理方法

    在项目AppDelegate.m中加入消息接收处理代理方法。

    1 - (void)application:(UIApplication *)application 
    2 didReceiveRemoteNotification:(NSDictionary *)userInfo
    3 {
    4     //在此处理接收到的消息。
    5     NSLog(@"Receive remote notification : %@",userInfo);
    6 }

    至此,IOS端的代码已经码完了。(^_^)

     

     

    服务器

    服务器端可以用php、Java、.net等语言实现。本文使用Java语言实现

    1、导入jar包

    工程建好之后,将JavaPNS_2.2.jar、javapns-jdk16-163.jar、bcprov-jdk16-145.jar这几个jar包拷贝到工程的lib目录下。

    2、生成服务器端所用的.p12文件(.net或Java等后台)

    在mac终端下执行以下指令:

    (1)、将aps_development.cer转换成aps_development.pem格式

    $ openssl x509 -in aps_development.cer -inform DER -out aps_development.pem -outform PEM  

    (2)、将p12格式的私钥转换成pem

    $ openssl pkcs12 -nocerts -out Push_Noenc.pem -in pushNotification.p12  

    (3)、创建p12文件

    $ openssl pkcs12 -export -in aps_development.pem -inkey Push_Noenc.pem -certfile pushNotification.certSigningRequest -name "aps_development" -out aps_development.p12  

    这样我们就得到了在.net或java等后台应用程序中使用的证书文件:aps_development.p12

    3、编写服务器端代码

      1 package com.push.server;
      2 import java.io.FileInputStream;
      3 import java.io.FileNotFoundException;
      4 import java.io.IOException;
      5 import java.util.ArrayList;
      6 import java.util.List;
      7 import java.util.Properties;
      8 
      9 import javapns.devices.Device;
     10 import javapns.devices.implementations.basic.BasicDevice;
     11 import javapns.notification.AppleNotificationServerBasicImpl;
     12 import javapns.notification.PushNotificationManager;
     13 import javapns.notification.PushNotificationPayload;
     14 import javapns.notification.PushedNotification;
     15 
     16 public class SendToAPNS {
     17     // 从客户端获取的deviceToken,在此为了测试,设一个固定值
     18     private static final String DEVICE_TOKEN = 
     19 
     20 "13b11050a3fc064b3692e25c0fbd3b774b39ecb0c55a51ff4fb1373e004577a0";
     21     List<String> deviceToken = new ArrayList<String>();
     22     
     23     public void send () {
     24         // 证书文件(.p12)在服务器端的目录
     25         String filePath = null;
     26         try {
     27             String path = this.getClass().getClassLoader().getResource("/").getPath();
     28             filePath = java.net.URLDecoder.decode(path,"utf-8");
     29         } catch (Exception e){
     30             e.printStackTrace();
     31         }
     32         System.out.println("filePath=" + filePath);
     33         String certificatePath = (filePath + "conf/ios_development.p12");
     34         // 获取ios_development.p12的密码
     35         Properties prop = new Properties();  
     36         FileInputStream fis = null;
     37         try {
     38             fis = new FileInputStream(filePath + "conf/pushmessage.properties");
     39              
     40         } catch (FileNotFoundException e) {
     41             // TODO Auto-generated catch block
     42             e.printStackTrace();
     43         }  
     44         try {
     45             prop.load(fis);
     46         } catch (IOException e) {
     47             // TODO Auto-generated catch block
     48             e.printStackTrace();
     49         }
     50         String certificatePassword = prop.getProperty("password");  
     51         
     52         // 构建发送的消息
     53         String message="{'aps':{'alert':'this is a push message'}}";
     54         
     55         // 设别标识
     56         deviceToken.add(DEVICE_TOKEN);
     57         // 发送消息
     58         sendpush(deviceToken, certificatePath, certificatePassword, message, 4, false);
     59     }
     60     
     61      /************************************************
     62      测试用URL gateway.sandbox.push.apple.com /2195 
     63      正式发布用URL gateway.push.apple.com / 2195 
     64     javaPNS_2.2.jar必须
     65      ***************************************************/
     66     /**
     67    * @param tokens   iphone设备的唯一标识
     68 
     69    * @param path   .p12证书文件的路径
     70 
     71    * @param password  .p12证书文件的密码
     72 
     73    * @param message   发送的消息内容
     74 
     75    * @param count
     76 
     77    * @param sendCount  true 一对一发送      false 群发
     78 
     79    */
     80     public void sendpush(List<String> tokens,String path, String password, String message,Integer 
     81 
     82 count,boolean sendCount) {
     83         try {
     84             // message:{"aps":{"alert":"一条新消息"}}
     85             PushNotificationPayload payLoad =  PushNotificationPayload.fromJSON(message);
     86             //payLoad.addAlert(message);
     87             payLoad.addBadge(count);
     88             payLoad.addSound("default");
     89             
     90             PushNotificationManager pushManager = new PushNotificationManager();
     91             // true 正式发布用URL
     92             // false 测试用URL
     93             pushManager.initializeConnection(new AppleNotificationServerBasicImpl(path, password, 
     94 
     95 false));
     96             List<PushedNotification> notifications = new ArrayList<PushedNotification>(); 
     97             // 推送方式
     98             if (sendCount) {
     99                 System.out.println("-------现在进行一对一推送-------");
    100                 Device device = new BasicDevice();
    101                 device.setToken(tokens.get(0));
    102                 PushedNotification notification = pushManager.sendNotification(device, payLoad, 
    103 
    104 true);
    105                 notifications.add(notification);
    106             } else {
    107                 System.out.println("------现在进行群发-------");
    108                 List<Device> device = new ArrayList<Device>();
    109                 for (String token : tokens) {
    110                     device.add(new BasicDevice(token));
    111                 }
    112                 notifications = pushManager.sendNotifications(payLoad, device);
    113             }
    114 
    115             List<PushedNotification> failedNotifications = PushedNotification.findFailedNotifications
    116 
    117 (notifications);
    118             List<PushedNotification> successfulNotifications = 
    119 
    120 PushedNotification.findSuccessfulNotifications(notifications);
    121             int failed = failedNotifications.size();
    122             int successful = successfulNotifications.size();
    123             
    124             if (successful > 0 && failed == 0) {
    125                 //log.debug("-----All notifications pushed success (" + 
    126 
    127 successfulNotifications.size() + "):");
    128                 System.out.println("-----All notifications pushed success (" + 
    129 
    130 successfulNotifications.size() + "):");
    131             } 
    132             else if (successful == 0 && failed > 0) {
    133                 //log.debug("-----All notifications pushed failed(" + failedNotifications.size() + 
    134 
    135 "):");
    136                 System.out.println("-----All notifications pushed failed(" + 
    137 
    138 failedNotifications.size() + "):");
    139             } 
    140             else if (successful == 0 && failed == 0) {
    141                 System.out.println("No notifications could be sent, probably because of a critical 
    142 
    143 error");
    144             } 
    145             else {
    146                 //log.debug("------Some notifications pushed failed (" + failedNotifications.size
    147 
    148 () + "):");
    149                 //log.debug("------Others pushed success(" + successfulNotifications.size() + 
    150 
    151 "):");
    152                 System.out.println("------Some notifications pushed failed (" + 
    153 
    154 failedNotifications.size() + "):");
    155                 System.out.println("------Others pushed success(" + successfulNotifications.size() 
    156 
    157 + "):");
    158             }
    159             pushManager.stopConnection();
    160             
    161         } catch (Exception e) {
    162             e.printStackTrace();
    163         }
    164     }
    165 }

     至此,服务器端代码也码完了。(^_^)

     

    APNS推送的特点

    从APNS的文档中大概总结了以下几点:

    1、提供单一发送和群发功能。

    2、当用户手机不在线(可能没有信号或者关机),APNs会存储转发,等用户在线时再发送。

    3、如果用户长时间不在线,这条信息会被忽略。

    4、如果用户不在线,通知会合并,只会保留最新的一条。

    5、payload,就是最后生成的那段Json,不得超过256字节。如果超过了,建议去掉一些不需要的参数,把alert,就是提示

    信息的字数减少。

    6、发送成功的没有返回,只有发送失败的才会返回。

    7、如果有error-response,那么这条之后的通知都需要重发。

    8、如果出错了,需要关闭当前的连接,并且重新连接再发。error-response中返回的通知ID,可以帮助我们找出哪条出错

    了,这样就能知道哪些需要重发了。

    9、不要反复多次连接、终止与APNS的连接,否则会被APNS拒绝连接。

    10、APNS的feedback service会返回那些已经卸载的设备的deviceToken。对于这些token,下次就不用再发了。可以节省

    点资源。需要注意的是:feedback的接口读取一次,APNS就会清空它的列表,下次再读取时,返回的就是这两次读取之间这

    段时间新产生的deviceToken

     

  • 相关阅读:
    【HDOJ】2774 Shuffle
    【POJ】2170 Lattice Animals
    【POJ】1084 Square Destroyer
    【POJ】3523 The Morning after Halloween
    【POJ】3134 Power Calculus
    【Latex】如何在Latex中插入伪代码 —— clrscode3e
    【HDOJ】4801 Pocket Cube 的几种解法和优化
    【HDOJ】4080 Stammering Aliens
    【HDOJ】1800 Flying to the Mars
    SQL语法
  • 原文地址:https://www.cnblogs.com/lidgblogs/p/4133324.html
Copyright © 2011-2022 走看看