[iOS] APNS: 如何check valid device token

本文应该有很多不对的地方,仅供参考 :)


一不小心,把sandbox和production的device token都放到同一个db table里了,大件事!因为sandbox的device token对于production是invalid的,反之亦然。


由于用的是c#的open source lib  APNS-Sharp  (https://github.com/Redth/APNS-Sharp) 来batch send notification。它的问题在于: 当你用它时,如果你要发送的device token list里有invalid的,那么在发送过程中,轮到这个token时就会close connection,之后的所有device tokens (即使是valid的)都不会收到notification 。我觉得这是APNS-Sharp的bug。 解决方法是在每个与apns的connection都只发送notification给一个device token,这样就不用担心是否存在 。或者寻找其他的open source lib??

我试过java版本的apns lib " javapns " (http://code.google.com/p/javapns/), 比apns sharp好多了,而且batch send notificaiton时,即使中间有invalid token,也不会影响valid token device收到notification。

那么, 有没有一个办法能从混合了sandbox and production device tokens的table里把production and sandbox的区分开来??
有! 用javapns就可以区分。原理就是用javapns + dev apns cert and password来batch send notification to device token in db table,对于那些invalid token,返回的notification object的isSuccessful是false,而且exception message为"invalide token"。
注意:不能使用javapns + production apns cert and password来做,因为这样会发送notification给production device,这样绝不能允许!

具体代码是:

[java]  view plain copy
  1. String certFilePath="/Users/issliao/tomson/certs/dev/dev_cert.p12";  
  2.     String certPassword="xxx";  
  3.     boolean isSendingProductionNotification=false;  
  4.          
  5.     try {  
  6.         //create payload  
  7.         PushNotificationPayload payLoad= new PushNotificationPayload();  
  8.         payLoad.addAlert("test");  
  9.         payLoad.addSound("default");  
  10.           
  11.         //add device token list  
  12.         List<Device> devices = new ArrayList<Device>();  
  13.         devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1112")); //wrong token  
  14.         devices.add(new BasicDevice("02a2fca6e3ec1ea62aa4b6a344fb9ad7f31f491b7099c0ddf7761cea6c563980")); //iphone pro  
  15.         devices.add(new BasicDevice("43fcc3cff12965bc45bf842bf9166fa60e8240c575d0aeb0bf395fb7ff86b466")); //ipod dev  
  16.         devices.add(new BasicDevice("e23411a04b4851c36efbdcd3f260df3acc1820b5ca6a4270ecb43b654cb8c022")); //ipod pro  
  17.         devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111")); //wrong token  
  18.           
  19.         /** 
  20.          * !!注意: 如果使用"sendNotifications" method一次过把notification send to all devices, 如果devices太多的话,会出现问题: 对于一些invalid device token也会显示send successful 
  21.          * 举个例子,你把50个"12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1112"添加到device list变量里试试,output会有以一些显示send successful 
  22.          *  
  23.          * 因此,解决方法就是每个由PushNotificationManager创建的connection只发送一个notificaiton给一个device token!下面的代码就是每个connection只发送notification给一个device! 
  24.          */  
  25.         for (Device device: devices){  
  26.             PushNotificationManager pushManager=new PushNotificationManager();  
  27.             /** 
  28.              * sandbox apns server: gateway.sandbox.push.apple.com   port 2195 
  29.              * production apns server: gateway.push.apple.com        port 2195 
  30.              */           
  31.             //connect to apns  
  32.             //3rd param: false = sandbox    true = production  
  33.             pushManager=new PushNotificationManager();  
  34.             pushManager.initializeConnection(new AppleNotificationServerBasicImpl(certFilePath, certPassword, isSendingProductionNotification));              
  35.               
  36.             List<PushedNotification> notifications=pushManager.sendNotifications(payLoad, device); //send to only one device  
  37.               
  38.             List<PushedNotification> failedNotifications=PushedNotification.findFailedNotifications(notifications);  
  39.             List<PushedNotification> successfulNotifications=PushedNotification.findSuccessfulNotifications(notifications);  
  40.               
  41.             for (PushedNotification successfulNoticiation : successfulNotifications) {  
  42.                 System.out.println("=====successful notification====");  
  43.                 System.out.println(successfulNoticiation.getDevice().getToken());                 
  44.             }  
  45.               
  46.             for (PushedNotification failedNoticiation : failedNotifications) {  
  47.                 System.out.println("=====fail notification====");  
  48.                 System.out.println(failedNoticiation.getDevice().getToken());                 
  49.                 if(failedNoticiation.getException()!=null)  
  50.                     System.out.println(failedNoticiation.getException().getMessage());  
  51.             }  
  52.         }  
  53.     } catch(Exception e){  
  54.         e.printStackTrace();  
  55.       

执行上面代码的输出中,successful的就是sandbox device token。
注意:输出中failed的并不一定是production device token。例如上面代码就带有1个很明显的dummy device token "12345...."。不管你是用production cert or dev cert来执行上述代码,对于它,都会是failed notification。因此上面代码的缺点就是只能找出sandbox device token,而不能找出既不是sandbox也不是production的device token,除非你用production cert来执行代码,但这在上面已经解释了,不能接受send test notification to production device token!


关于javapns (v2.2)

* send single notification

PushNotificationManager的sendNotification有问题,不管device token是否valid,返回的notification的“isSuccessful"都返回true。见下例,device token明显是错的,output依然是true

[java]  view plain copy
  1. Device device=new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111");  
  2. PushedNotification notification = pushManager.sendNotification(device, payLoad, true);  
  3. System.out.println(notification.isSuccessful());  

因此即使是send单个的notification,都建议用batch send method "sendNotifications"

[java]  view plain copy
  1. Device device=new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111");  
  2. List<PushedNotification> notifications = pushManager.sendNotifications(payLoad, device);  
  3. List<PushedNotification> failedNotifications=PushedNotification.findFailedNotifications(notifications);             
  4.               
  5. for (PushedNotification failedNoticiation : failedNotifications) {  
  6.     System.out.println(failedNoticiation.getDevice().getToken());             
  7.     if(failedNoticiation.getException()!=null)  
  8.         System.out.println(failedNoticiation.getException().getMessage());  
  9. }  


* batch send notification to multiple devices

正如之前提到的,如果使用"sendNotifications" method一次过把notification send to all devices, 如果devices太多的话,会出现问题: 对于一些invalid device token也会显示send successful. (我试过一次性send 10个device没问题,send 50个就有问题)

举个例子,你把50个"12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1112"添加到device list变量里试试,output会有以一些显示send successful. 因此,解决方法就是每个由PushNotificationManager创建的connection只发送一个notificaiton给一个device token


不过每个connection只发送给一个device token有严重的缺点:

* apns feedback 可能没用了

* apns server可能不允许短时间频繁创建connection,可能被认为是ddos attack。


最优的是(前提是没有invalid token) 每个connection发送200条。超过200条就另建connection.

ref: 

http://stackoverflow.com/questions/6819315/max-number-of-devices-to-send-to-apns-socket-sever

http://www.v2ex.com/t/23837



[java]  view plain copy
  1. List<Device> devices = new ArrayList<Device>();  
  2. devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111")); //wrong token  
  3. devices.add(new BasicDevice("02a2fca6e3ec1ea62aa4b6a344fb9ad7f31f491b7099c0ddf7761cea6c563921")); //iphone pro  
  4. devices.add(new BasicDevice("43fcc3cff12965bc45bf842bf9166fa60e8240c575d0aeb0bf395fb7ff86b421")); //ipod dev  
  5. devices.add(new BasicDevice("e23411a04b4851c36efbdcd3f260df3acc1820b5ca6a4270ecb43b654cb8c021")); //ipod pro  
  6. devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111")); //wrong token  
  7.   
  8. for (Device device: devices){  
  9.     PushNotificationManager pushManager=new PushNotificationManager();  
  10.     /** 
  11.      * sandbox apns server: gateway.sandbox.push.apple.com   port 2195 
  12.      * production apns server: gateway.push.apple.com        port 2195 
  13.      */           
  14.     //connect to apns  
  15.     //3rd param: false = sandbox    true = production  
  16.     pushManager=new PushNotificationManager();  
  17.     pushManager.initializeConnection(new AppleNotificationServerBasicImpl(certFilePath, certPassword, isSendingProductionNotification));              
  18.       
  19.     List<PushedNotification> notifications=pushManager.sendNotifications(payLoad, device); //send to only one device  
  20.       
  21.     List<PushedNotification> failedNotifications=PushedNotification.findFailedNotifications(notifications);  
  22.     List<PushedNotification> successfulNotifications=PushedNotification.findSuccessfulNotifications(notifications);  
  23.       
  24.     for (PushedNotification successfulNoticiation : successfulNotifications) {  
  25.         System.out.println("=====successful notification====");  
  26.         System.out.println(successfulNoticiation.getDevice().getToken());                 
  27.     }  
  28.       
  29.     for (PushedNotification failedNoticiation : failedNotifications) {  
  30.         System.out.println("=====fail notification====");  
  31.         System.out.println(failedNoticiation.getDevice().getToken());                 
  32.         if(failedNoticiation.getException()!=null)  
  33.             System.out.println(failedNoticiation.getException().getMessage());  
  34.     }  
  35. }  


* Feedback

[java]  view plain copy
  1. String certFilePath="/Users/tomson/certs/production/aps_production.p12";  
  2. String certPassword="XXXX";  
  3. boolean isSendingProductionNotification=true;  
  4.           
  5. try {  
  6.     List<Device> inactiveDevices = Push.feedback(certFilePath, certPassword, isSendingProductionNotification);  
  7.     for (Device inactiveDevice : inactiveDevices) {  
  8.         System.out.println(inactiveDevice.getToken());                
  9.     }  
  10. catch (Exception e) {  
  11.     e.printStackTrace();  
  12. }  


注意: 
* feedback返回的只是那些inactive device token list。而不会包含那些你曾经send notification to invalid token的device list。
* feedback只会返回最近那次send notification的inactive token list。当你再次send notification时之前的inactive token list就会清空。
* 当你执行上述代码来get feedback后,server side就会清空inactive token list,即你再get feedback时inactive token list为empty。
举个例子:batch send一个notification给4个device tokens with production apns cert:
> device A with production token
> device A with dev token
> device B with production token,但device B已经卸载了app
> 无效的假device token。
发送notificaiton之后,执行上面的feedback代码,那么返回的inactive token list中只会包含device B  production token,而不会包含假devie token and device A dev token。

* inactive device token通常是指那些安装过app又卸载了app的device。官方解释为
If a provider attempts to deliver a push notification to an application, but the application no longer exists on the device, the device reports that fact to Apple Push Notification Service. This situation often happens when the user has uninstalled the application. If a device reports failed-delivery attempts for an application, APNs needs some way to inform the provider so that it can refrain from sending notifications to that device. Doing this reduces unnecessary message overhead and improves overall system performance.
For this purpose Apple Push Notification Service includes a feedback service that APNs continually updates with a per-application list of devices for which there were failed-delivery attempts. The devices are identified by device tokens encoded in binary format. Providers should periodically query the feedback service to get the list of device tokens for their applications, each of which is identified by its topic. Then, after verifying that the application hasn’t recently been re-registered on the identified devices, a provider should stop sending notifications to these devices.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值