消息推送之APNS

利用APNS進行消息推送php

原理java

APNS 是Apple Push Notification Service(Apple Push服務器)的縮寫,是蘋果的服務器。ios

APNS推送能夠分爲三個階段:服務器

第一階段:推送服務器應用程序把要發送的消息、目的iPhone的標識打包,發給APNS。app

第二階段:APNS在自身的已註冊推送服務的iPhone列表中,查找有相應標識的iPhone,並把消息發到iPhone。iphone

第三階段:iPhone把發來的消息傳遞給相應的應用程序,而且按照設定彈出推送通知。測試

詳細流程以下:this

一、首先是應用程序註冊消息推送服務。spa

二、APNS嚮應用程序返回deviceToken。.net

三、應用程序將deviceToken發送給推送服務端程序。

四、服務端程序向APNS服務發送消息。

五、APNS服務將消息發送給iPhone應用程序。

 

證書生成

網上有不少關於證書生成的詳細步驟,這裏再也不說明了。

最終生成的證書共包含下面四個

一、pushNotification.certSigningRequest

二、aps_development.cer(下載生成的支持推送服務的證書。)

三、pushNotificationDevprofile.mobileprovision

四、pushNotification.p12

 

下面直接上代碼。

 

客戶端

一、應用程序註冊消息推送服務

在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 }

 

二、接收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 } 

 

三、接收消息的處理方法

在項目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語言實現

一、導入jar包

工程建好以後,將JavaPNS_2.2.jar、javapns-jdk16-163.jar、bcprov-jdk16-145.jar這幾個jar包拷貝到工程的lib目錄下。

二、生成服務器端所用的.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

三、編寫服務器端代碼

  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的文檔中大概總結了如下幾點:

一、提供單一發送和羣發功能。

二、當用戶手機不在線(可能沒有信號或者關機),APNs會存儲轉發,等用戶在線時再發送。

三、若是用戶長時間不在線,這條信息會被忽略。

四、若是用戶不在線,通知會合並,只會保留最新的一條。

五、payload,就是最後生成的那段Json,不得超過256字節。若是超過了,建議去掉一些不須要的參數,把alert,就是提示

信息的字數減小。

六、發送成功的沒有返回,只有發送失敗的纔會返回。

七、若是有error-response,那麼這條以後的通知都須要重發。

八、若是出錯了,須要關閉當前的鏈接,而且從新鏈接再發。error-response中返回的通知ID,能夠幫助咱們找出哪條出錯

了,這樣就能知道哪些須要重發了。

九、不要反覆屢次鏈接、終止與APNS的鏈接,不然會被APNS拒絕鏈接。

十、APNS的feedback service會返回那些已經卸載的設備的deviceToken。對於這些token,下次就不用再發了。能夠節省

點資源。須要注意的是:feedback的接口讀取一次,APNS就會清空它的列表,下次再讀取時,返回的就是這兩次讀取之間這

段時間新產生的deviceToken

相關文章
相關標籤/搜索