利用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