http://blog.163.com/ray_jun/blog/static/167053642201231144540434/ (原帖地址)
客户端发送device token给推送服务器,服务器将这个码发送给苹果服务器。客户端等待苹果的推送服务。
后台服务器上的方法send_apns_to_devices方法,遍历所有注册推送服务的机器,将deviceToken号发给苹果2195端口,这样只要有一个客户端第一次启动app并且接收了APNS服务,那么所有的机器都会收到苹果的服务。除非用户关闭这个功能。
所以设置意见反馈服务器就很重要。客户端需要定期的给feedback服务器建立连接。使本地的feedback服务器连接苹果的2196端口,苹果服务器把卸载程序的机器号返回给服务器。然后服务器对deviceToken转换成64位的字符串。并且从SQL服务器上删除。这样,当有新用户注册苹果的APNS消息的时候,本地服务器遍历所有的deviceToken的时候,就能过滤掉删除程序的设备号。然后建立SSL连接的时候,苹果服务器就停止給已经删除程序的app发送推送通知服务了。
XCode项目(模拟发送远程通知):
https://github.com/stefanhafeneger/PushMeBaby
一、CSR文件
1、生成Certificate Signing Request(CSR)
2、填写你的邮箱和常用名称,并选择保存到硬盘。
点击继续:
这样就在本地生成了一个Push.certSigningRequest文件。
二、p12文件
1、导出密钥。
2、输入你的密码。
这样就生成了一个Push.p12文件。
三、SSL certificate文件
1、用你付过费的帐号登录到iOS Provisioning Portal,并新建一个App ID,这个过程可以参考:iOS应用的真机调试,这样就会生成下面这条记录:
2、点击右侧的Configure:
3、点击Development Push SSL Certificate一行后的Configure:
4、点击Continue:
5、选择前面生成好的Push.certSigningRequest文件,点击Generate,出现如下所示的页面:
6、点击Continue:
7、点击Download,并将文件命名为aps_developer_identity.cer;双击aps_developer_identity.cer 文件导入Keychain中。
8、点击Done,你会发现状态变成了Enabled:
注意:有的App ID的Apple Push Notification service列是灰色的,并且不允许使用Configure按钮,这是因为APNS不支持带通配符的App ID。
到现在为止,我们已经生成了三个文件:
1、Push.certSigningRequest
2、Push.p12
3、aps_developer_identity.cer
在项目的AppDelegate中的didFinishLaunchingWithOptions方法中加入下面的代码:
- [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge];
通过registerForRemoteNotificationTypes方法,告诉应用程序,能接受push来的通知。
在项目的AppDelegate中添加下面的方法来获取deviceToken:
-
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(@"userInfo=%@",userInfo);
}
- - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
- {
- NSLog(@"My token is:%@", [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]);
- }
- - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
- {
- NSLog(@"Failed to get token, error:%@", [error localizedDescription]);
- }
获取到的deviceToken,我们可以提交给后台应用程序,发送通知的后台应用程序除了需要知道deviceToken之外,还需要一个与APNS连接的证书。
这个证书可以通过我们前面生成的两个文件中得到。
1、将aps_developer_identity.cer转换成aps_developer_identity.pem格式
- openssl x509 -in aps_developer_identity.cer -inform DER -out aps_developer_identity.pem -outform PEM
2、将p12格式的私钥转换成pem
3、创建p12文件
- openssl pkcs12 -export -in aps_developer_identity.pem -inkey Push_Noenc.pem -certfile Push.certSigningRequest -name "aps_developer_identity" -out aps_developer_identity.p12
这样我们就得到了在.net或java等后台应用程序中使用的证书文件:aps_developer_identity.p12
如果后台应用是php的话,在Mac上启动 Keychain助手,然后在login keychain中选择 Certificates分类。你将看到一个可扩展选项“Apple Development Push Services”
- 扩展此选项然后右击“Apple Development Push Services” > Export “Apple Development Push Services ID123”。保存为 apns-dev-cert.p12 文件。
- 扩展“Apple Development Push Services” 对“Private Key”做同样操作,保存为 apns-dev-key.p12 文件。
- 需要通过终端命令将这些文件转换为PEM格式:
- openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12 - 如果你想要移除密码,要么在导出/转换时不要设定或者执行:openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem
- 最后,你需要将键和许可文件合成为apns-dev.pem文件,此文件在连接到APNS时需要使用:cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem
在.net应用程序中发送通知。
有个开源的类库:apns-sharp。
地址是:http://code.google.com/p/apns-sharp/。
我们下载源代码,对里面的JdSoft.Apple.Apns.Notifications做相应的调整就能用了。
我们根据DeviceToken和p12File对JdSoft.Apple.Apns.Notifications.Test代码做相应的调整,如下图。
这样就OK了。
效果:
通知的代码:
- for (int i = 1; i <= count; i++)
- {
- //Create a new notification to send
- Notification alertNotification = new Notification(testDeviceToken);
- alertNotification.Payload.Alert.Body = string.Format("Testing {0}...", i);
- alertNotification.Payload.Sound = "default";
- alertNotification.Payload.Badge = i;
- //Queue the notification to be sent
- if (service.QueueNotification(alertNotification))
- Console.WriteLine("Notification Queued!");
- else
- Console.WriteLine("Notification Failed to be Queued!");
- //Sleep in between each message
- if (i < count)
- {
- Console.WriteLine("Sleeping " + sleepBetweenNotifications + " milliseconds before next Notification...");
- System.Threading.Thread.Sleep(sleepBetweenNotifications);
- }
- }
在 PHP 应用程序中发送通知:
使用 PHP 很容易根据数组并 转换成 JSON而创建载荷:
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload = json_encode($payload);
显示 $payload 的内容可以看到传送到APNS 的 JSON字符串:
{
"aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" }
}
这将使消息显示于设备上,触发提升声音并将“1”置于程序图标上。默认按钮“Close”和“View”同时会显示于弹出窗口上。
对于 Server Density iPhone程序而言,让用户按下“View”直接进入产生此提示的服务器是很重要的,所以我们增加了额外的自定义值:
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload['server'] = array('serverId' => $serverId, 'name' => $name);
$output = json_encode($payload);
当用户按下“View”后,自定义server值将被传递到设备中的程序。JSON 值如下:
{
"aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" },
"server" : { "serverId" : 1, "name" : "Server name")
}
256字节的限制适用于整个载荷,包括自定义字典集。
原生接口
在Server Density中,一旦产生了一条提示,将建立一个载荷并插入队列中。因此有必要时我们可以同时发送多个载荷。
Apple推荐使用这种方法,因为如果你在发送各载荷时频繁连接和断开,APNS有可能会封锁你的IP。
如Apple 描述:
原生接口使用原生socket,具有二进制内容,采用数据流技术,不产生回馈。
打开连接
打开连接的 PHP 5代码如下:
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = 'apns-dev.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2,
STREAM_CLIENT_CONNECT, $streamContext);
如果发送错误,你可以参考$errorString。它也包括了SSL许可证不正确时的详细信息。
许可证文件处于执行的PHP代码的当前工作目录下,如果需要你可指定其绝对路径。
注意测试时应该使用开发许可证及sandbox。成品主机名为 gateway.push.apple.com ,而且你必须使用不同的产品许可证。
发送载荷
在此,我们循环整个载荷队列进行发送。构建发送到APNS的二进制内容简单示例如下:
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) .
chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
注意 $deviceToken 是从数据库中提取并去除空格得到的。我们还应该检查是否$payload超过256个字节。
$apnsMessage 包括了正确的二进制载荷,而fwrite 将载荷写入当前活动的数据流连接中。
完成后,应关闭连接:
socket_close($apns);
fclose($apns);
php-apns