当我们在客户端实现推送跳转需求后,但是服务器还没有做好后台的配置,此时需要测试收到推送消息之后能否按照预期,执行我们的代码。但在这个情况下如何去做呢。这里可以提供给大家两个选择。
1. 封装好处理逻辑的接口,在程序启动或者某个时刻模拟数据,直接调用处理逻辑的接口,查看是否符合预期。该情况只适用于测试跳转逻辑,不能测试数据解析的正确性(例如字段名是否正确、字段的类型是否正确等)。
例如,我们处理推送消息的函数都写在了Appdelegate.m中的- (void)dealWithNotification:(NSDictionary*)userInfo函数里
那么我们就需要来构造数据进行模拟推送,比如这样:
// 用于模拟器测试推送 方便快速测试推送问题 使用这个函数测试完,请务必在真实推送情况下测试
- (void)sendNotificationSelf:(RemoteNotificationType)type
{
NSDictionary * dictionary = nil;
if (type == kRemoteNotificationExpression)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(3), @"flag", @(1033), @"secondClassifyId", nil];
else if (type == kRemoteNotificationDwaft)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(5), @"flag", @"宝贝计划", @"templateName", @(126), @"templateId", nil];
else if (type == kRemoteNotificationCharacter)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(6), @"flag", nil];
else if (type == kRemoteNotificationSoundExpressionCategory)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(8), @"flag", @(176), @"classifyId", @"", @"classifyName", nil];
else if (type == kRemoteNotificationSoundExpressionSubjectDetail)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(9), @"flag", @(18), @"subjectId", @"动物成精了", @"subjectName", nil];
else if (type == kRemoteNotificationMessageCenter)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(10), @"flag", nil];
else if (type == kRemoteNotificationFansList)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(11), @"flag", nil];
else if (type == kRemoteNotificationStatusDetail)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(12), @"flag", @"" , @"sid", nil];
else if (type == kRemoteNotificationUserHomePage)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(13), @"flag", [UserInfo getSelf].uid, @"uid", nil];
[self dealWithNotification:dictionary];
}
这段代码的作用非常简单,其实就是根据预设好的推送类型,来模拟不同的推送数据,进而调用推送函数的统一处理方法。
我们可以在应用启动的时候调用来模拟应用未开启时点击推送的效果,在点击某个按钮时调用来模拟应用在前台收到推送 的效果,写一个延时来调用然后点击home键,来模拟应用在后台收到推送的效果了。
怎么样,看起来还不错吧,基本上可以解决问题,那还有没有更好的方案了,请继续看。
2. 既然服务器还没有做好相关的配置,那么为什么我们自己做一个阉割版的服务器,只做推送测试呢?OK,那就了解一下,iOS推送通知的基本原理。
iOS推送通知的基本原理
苹果的推送服务是由自己专门的推送服务器APNs(Apple Push Notification service)来完成的,过程是APNs收到我们自己的应用服务器发出的被推送的消息,将这条消息推送到制定的iOS设备上,然后再由iOS设备通知到我们的应用程序,我们将会以通知或者声音的形式收到推送回来的消息。iOS远程推送的前提是,装有我们应用程序的iOS设备,要向APNs服务器注册,注册成功后,APNs服务器将会给我们返回一个deviceToken,我们获取到这个token后,将它发送给我们自己的应用服务器。当我们需要推送消息时,我们的应用服务器将消息按照指定的格式进行打包,然后结合iOS设备的deviceToken一起发给APNs服务器。iOS设备会和APNs服务器维持一个基于TCP的长连接,APNs服务器将新消息推送到iOS设备上,然后在设备屏幕上进行显示。
其实总结下来过程如下:
1. 客户端向APNs申请注册消息推送服务。
2. APNs服务器接受到请求后,并将deviceToken返回给设备上的客户端
3. 客户端将deviceToken发送给自己的后台服务器,后台接受后并储存。
4. 后台服务器向APNs服务器发送推送消息
5. APNs服务器接受后,将消息发送给deviceToken对应的设备
我们来看上面的过程中服务器要做的就是接受并储存客户端发送的deviceToken,以便之后再要发送推送时,与APNs发送推送消息。所以如果要哦让我们自己做一个阉割版的服务端也很简单,首先deviceToken,只要我们不卸载应用,那么他是不会变化的。所以我们只要在XCode中输出deviceToken并且记录下来就可以解决存储的问题,那么后台服务器怎么与APNs服务器交互的呢,其实只需要跟APNs进行SSL连接,把我们APNs预期的格式的数据传递过去就可以了。基于Mac平台,我们可以选择PHP来实现,毕竟Mac自带了PHP环境,使用起来很方便(况且PHP是世界上最好的语言J)。
使用PHP进行通信的话,就需要使用.pem文件。
.PEM文件是什么?
如果把SSL系统比喻成工商局系统。
首先有SSL就有CA,certificateauthority。证书局,同于制作、认证证书的第三方机构,我们假设应用执照非常难制作,就像身份证一样,需要有制作公司来提供,并且提供技术帮助工商局验证执照的真伪。
然后CA是可以有很多的,也就是可以有多个制证公司,但是工商局只有一个,它来说哪个制证公司是可信的,哪些是假的,需要打击。在SSL的世界中,微软、谷歌和Mozilla扮演了一部分的这个角色。也就是说,IE、Chrome、Firefox中内置有一些CA,经过这些CA颁发,验证过的证书都是可信的,否则就会提示你不安全。
要开店的老板去申请营业执照的时候是需要提交他的身份证的,然后搬出来的营业执照有他的照片和名字。身份证相当于私钥,营业执照就是证书,Ceritificate,.cer文件。
然后私钥和公钥他们在数据加密层面,数据的流向是这样的。
消息-->[公钥]-->加密后的信息-->[私钥]-->消息
公钥是可以随便扔给别人的,他把消息加了密传递给我。可以这样理解,我有一个箱子,一把锁和一把钥匙,我把箱子和开着的锁给别人,他写信放到箱子里面,锁上,然后传递回我受伤的途中谁都是打不开箱子的,只有我可以用原来的钥匙打开,这就是SSL,公钥、私钥传递加密消息的方式。这里的密钥就是.key文件。
PEM,它是由RFC1421至1424定义的一种数据格式。其实里面的.cert和.key文件都是PEM格式的,只不过在有些系统(比如Windows)里面会根据扩展名的不同而做不同的事情。所以当看到.pem文件时,它里面的内容可能是certificate也可能是key,也可能两个都有,要看具体情况,可以通过OpenSSL查看。而在与APNs的通讯中,我们需要传递的是同时包含.cert和.key的文件。苹果通过这个来确定你是否是开发者本人。
如何得到.pem文件
1. 如果在Mac上钥匙串访问中能找到Apple Development iOS Push Server证书的话,可以在证书上面右键导出生成apns_dev_cert.p12。如果没有的话,可以在苹果开发者中心生成aps_development.cer文件,然后下载,双击导入钥匙串。
2. 然后在钥匙串访问中找到Apple DevelopmentiOS Push Server密钥,右键生成apns_dev_key.p12。
3. 打开终端,进入p12证书所在的目录,输入openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12 生成apns-dev-cert.pem
4. 输入openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12 生成apns-dev-key.pem,要输入密码,记住这个密码
5. 把两个.pem文件合并 cat apns-dev-cert.pem apns-dev-key.pem > apns-dev.pem
这样就得到了开发环境的pem文件了,如果是生产环境,那么需要选择生环境的证书并做相同的操作,得到apns-dis-cert.pem即可。
可以通过 openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert apns-dev-cert.pem -key apns-dev-key.pem 测试证书是否正常。执行完毕后,直接输入任意字符,回车,出现closed,表示可用。否则打印错误信息。
如果出现上面的错误信息,那么可能是OpenSSL的cert.pem文件问题,可以在终端中输入命令php -r "print_r(openssl_get_cert_locations());"来查看default_cert_file的路径,并且替换掉,可用版本的cert.pem在这里(链接:https://pan.baidu.com/s/1IlOqpWK-ugenEEjaWqNf9A 密码:7qmc),替换前打印下原文件的权限,替换后修改为同样的权限即可。
编写PHP
.pem有了,那么我们需要做的就是编写PHP了。直接送上源码,需要注意的是修改pass变量为刚才生成.pem的密码,替换deviceToken为测试设备的token。如果是在开发环境,那么选择ssl://gateway.sandbox.push.apple.com:2195,如果是生产环境,那么选择ssl://gateway.push.apple.com:2195。代码如下:
<?php
$deviceToken= 'f020999681cc383f6a5e3efb8e3321912808ade966d20a3ec9696fbd5fa24e4c'; //没有空格
$type = 1; // 1 配图 2 逗图工坊 3 文字表情
$body = 0;
if ($type == 1)
{
$body = array( "aps" =>array("alert" => '配图推送测试',"badge" => 1,"sound"=>'default'),
"flag" => "2",
"imageListName" => "我快疯了!",
"imageListId" => 2589); //推送方式,包含内容和声音
}
elseif ($type == 2)
{
$body = array( "aps" => array("alert" => '逗图工坊推送测试',"badge" => 1,"sound"=>'default'),
"flag" => "5",
"templateName" => "二次元",
"templateId" => 477); //推送方式,包含内容和声音
}
elseif ($type == 3) {
$body = array( "aps" =>array("alert" => '文字表情推送测试',"badge" => 1,"sound"=>'default'),
"flag" => "6"); //推送方式,包含内容和声音
}
elseif ($type == 4)
{
$body = array( "aps" =>array("alert" => '表情推送测试',"badge" => 1,"sound"=>'default'),
"flag" => "3",
"secondClassifyName" => "斗图应答",
"secondClassifyId" => 1033); //推送方式,包含内容和声音
}
$ctx = stream_context_create();
//如果在Windows的服务器上,寻找pem路径会有问题,路径修改成这样的方法:
//$pem = dirname(__FILE__) . '/' . 'apns-dev.pem';
//linux 的服务器直接写pem的路径即可
stream_context_set_option($ctx,"ssl","local_cert","apns-dev.pem");
$pass = "123456";
stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
//此处有两个服务器需要选择,如果是开发测试用,选择第二名sandbox的服务器并使用Dev的pem证书,如果是正是发布,使用Product的pem并选用正式的服务器
$fp = stream_socket_client("ssl://gateway.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
// $fp = stream_socket_client("ssl://gateway.sandbox.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
echo "Failed to connect $err $errstrn";
return;
}
print "Connection OK\n";
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack("H*", str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
echo "sending message :" . $payload ."\n";
fwrite($fp, $msg);
fclose($fp);
?>
执行测试
最后把push.php和apns-dev.pem放到同一目录下,进入命令行输入php push.php即可。
参考文献:
1.https://www.zhihu.com/question/29620953
2.http://blog.sina.com.cn/s/blog_6f9a9718010128hi.html
3.https://www.cnblogs.com/cocoajin/p/3470704.html