[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,这样绝不能允许!

具体代码是:

	String certFilePath="/Users/issliao/tomson/certs/dev/dev_cert.p12";
		String certPassword="xxx";
		boolean isSendingProductionNotification=false;
        
		try {
			//create payload
			PushNotificationPayload payLoad= new PushNotificationPayload();
			payLoad.addAlert("test");
			payLoad.addSound("default");
			
			//add device token list
			List<Device> devices = new ArrayList<Device>();
			devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1112")); //wrong token
			devices.add(new BasicDevice("02a2fca6e3ec1ea62aa4b6a344fb9ad7f31f491b7099c0ddf7761cea6c563980")); //iphone pro
			devices.add(new BasicDevice("43fcc3cff12965bc45bf842bf9166fa60e8240c575d0aeb0bf395fb7ff86b466")); //ipod dev
			devices.add(new BasicDevice("e23411a04b4851c36efbdcd3f260df3acc1820b5ca6a4270ecb43b654cb8c022")); //ipod pro
			devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111")); //wrong token
			
			/**
			 * !!注意: 如果使用"sendNotifications" method一次过把notification send to all devices, 如果devices太多的话,会出现问题: 对于一些invalid device token也会显示send successful
			 * 举个例子,你把50个"12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1112"添加到device list变量里试试,output会有以一些显示send successful
			 * 
			 * 因此,解决方法就是每个由PushNotificationManager创建的connection只发送一个notificaiton给一个device token!下面的代码就是每个connection只发送notification给一个device!
			 */
			for (Device device: devices){
				PushNotificationManager pushManager=new PushNotificationManager();
				/**
				 * sandbox apns server: gateway.sandbox.push.apple.com   port 2195
				 * production apns server: gateway.push.apple.com		 port 2195
				 */			
				//connect to apns
				//3rd param: false = sandbox    true = production
				pushManager=new PushNotificationManager();
				pushManager.initializeConnection(new AppleNotificationServerBasicImpl(certFilePath, certPassword, isSendingProductionNotification));			
				
				List<PushedNotification> notifications=pushManager.sendNotifications(payLoad, device); //send to only one device
				
				List<PushedNotification> failedNotifications=PushedNotification.findFailedNotifications(notifications);
				List<PushedNotification> successfulNotifications=PushedNotification.findSuccessfulNotifications(notifications);
				
				for (PushedNotification successfulNoticiation : successfulNotifications) {
					System.out.println("=====successful notification====");
					System.out.println(successfulNoticiation.getDevice().getToken());				
				}
				
				for (PushedNotification failedNoticiation : failedNotifications) {
					System.out.println("=====fail notification====");
					System.out.println(failedNoticiation.getDevice().getToken());				
					if(failedNoticiation.getException()!=null)
						System.out.println(failedNoticiation.getException().getMessage());
				}
			}
		} catch(Exception e){
			e.printStackTrace();
		

执行上面代码的输出中,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

Device device=new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111");
PushedNotification notification = pushManager.sendNotification(device, payLoad, true);
System.out.println(notification.isSuccessful());

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

Device device=new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111");
List<PushedNotification> notifications = pushManager.sendNotifications(payLoad, device);
List<PushedNotification> failedNotifications=PushedNotification.findFailedNotifications(notifications);			
			
for (PushedNotification failedNoticiation : failedNotifications) {
	System.out.println(failedNoticiation.getDevice().getToken());			
	if(failedNoticiation.getException()!=null)
		System.out.println(failedNoticiation.getException().getMessage());
}


* 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



			List<Device> devices = new ArrayList<Device>();
			devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111")); //wrong token
			devices.add(new BasicDevice("02a2fca6e3ec1ea62aa4b6a344fb9ad7f31f491b7099c0ddf7761cea6c563921")); //iphone pro
			devices.add(new BasicDevice("43fcc3cff12965bc45bf842bf9166fa60e8240c575d0aeb0bf395fb7ff86b421")); //ipod dev
			devices.add(new BasicDevice("e23411a04b4851c36efbdcd3f260df3acc1820b5ca6a4270ecb43b654cb8c021")); //ipod pro
			devices.add(new BasicDevice("12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde1111")); //wrong token
			
			for (Device device: devices){
				PushNotificationManager pushManager=new PushNotificationManager();
				/**
				 * sandbox apns server: gateway.sandbox.push.apple.com   port 2195
				 * production apns server: gateway.push.apple.com		 port 2195
				 */			
				//connect to apns
				//3rd param: false = sandbox    true = production
				pushManager=new PushNotificationManager();
				pushManager.initializeConnection(new AppleNotificationServerBasicImpl(certFilePath, certPassword, isSendingProductionNotification));			
				
				List<PushedNotification> notifications=pushManager.sendNotifications(payLoad, device); //send to only one device
				
				List<PushedNotification> failedNotifications=PushedNotification.findFailedNotifications(notifications);
				List<PushedNotification> successfulNotifications=PushedNotification.findSuccessfulNotifications(notifications);
				
				for (PushedNotification successfulNoticiation : successfulNotifications) {
					System.out.println("=====successful notification====");
					System.out.println(successfulNoticiation.getDevice().getToken());				
				}
				
				for (PushedNotification failedNoticiation : failedNotifications) {
					System.out.println("=====fail notification====");
					System.out.println(failedNoticiation.getDevice().getToken());				
					if(failedNoticiation.getException()!=null)
						System.out.println(failedNoticiation.getException().getMessage());
				}
			}


* Feedback

		String certFilePath="/Users/tomson/certs/production/aps_production.p12";
		String certPassword="XXXX";
		boolean isSendingProductionNotification=true;
				
		try {
			List<Device> inactiveDevices = Push.feedback(certFilePath, certPassword, isSendingProductionNotification);
			for (Device inactiveDevice : inactiveDevices) {
				System.out.println(inactiveDevice.getToken());				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}


注意: 
* 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.
















   

你可能感兴趣的:([iOS] APNS: 如何check valid device token)