IOS 本地推送和远程推送
最近在研究iOS的推送问题,遇到了好多问题,经过很多大神的文章指点最终整理了一下,放在这里和大家分享。
准备工作
首先要有一台苹果的设备,模拟器是不支持推送的,所以你需要一台iphone,ipod touch或者ipad。
我们的客户端与苹果服务器之间和我们自己的服务器与苹果服务器之间都需要证书来进行链接。下面我们来开始进入证书的制作过程。
一 CSR文件
首先我们要有生成一个Certificate Signing Request(也就是CSR)的请求文件。
在应用程序里的使用工具中找到钥匙串访问。
选择从证书颁发机构请求证书
填上你的邮箱和常用名,常用名要记一下,一会会用到。然后选择保存到磁盘,继续,保存位置在桌面,点击存储。
到这里点击完成后我们会在桌面上看到一个CertificateSigningRequest.certSigningRequest的请求文件,也就是我们说的CSR文件。在我们生成CSR文件的同时,会在钥匙串访问中生成一对秘钥,名称为刚才我们填写的常用名
二、 创建Appid
(这里我为了大家能看清楚,已经把之前的证书事先吊销了)
到https://developer.apple.com/devcenter/ios/index.action 登录后,选择Certificates
点击左侧的App IDs,找到我们要做推送功能的程序的id(如果没有的话要先New一个。注意,这里的App ID必须不能是通配的,通配的不可以做推送)。点击Configure
点击右上角的“+”号,显示如下:
Name起个有意义的名字,bundle ID 就像当于安卓当中的包名吧,我是这样的理解的,这个一定要写对!然后会出现这样的界面
点“submit”-----"done",然后点击“Certificates”,下面有"development"(这个是测试要用到的)和"Production"(发布到用到),两个生成的原理一样,就先生成测试证书吧,点右上角的“+”号,出现如图
因为我们生成的是测试版本的,就选Development下的App Push Notification Server(Sandbox),点击继续,出现下图:
App ID选择刚刚我们创建的那个,点击继续出现下图:
再点继续:
点击“Choose File”选择之前生成的CSR文件,创建好后下载到电脑。安装
四、下载Provisioning证书,下载之前要创建一个,也选择测试版本的,和上面的步骤差不多,注意App ID不要选错就好了。就不上图了。
创建好后下载到电脑,双击安装就好了!
五 从钥匙串访问中导出秘钥
打开钥匙串访问,找到我们的专用秘钥(专用秘钥的名称就是我们在最开始生成CSR请求的时候填写的常用名)
右键选择导出,
导出的文件名我们叫做push吧
在这里需要输入一个密码来对文件进行加密。这里我们选择123456,当然你也可以自己选择是什么,但是这个密码必须要铭记,切记!
然后输入你电脑的密码,点击允许。这样我们就在桌面上生成了一个Push.p12文件。到此为止,我们在桌面上一共生成了三个文件。一个是CSR请求文件,一个是aps_development .cer的SSL证书文件,还有一个刚才生成的push.p12秘钥文件。
现在我们的准备工作已经做完了。要开始对生成的文件进行处理了。原因上面已经解释过,因为我们的服务器链接苹果服务器也是需要证书的,但是我们直接生成的证书windows系统(我们一般的服务器都是win系统的)是不识别的,所以我们需要生成一个后缀为pem的带证书带秘钥的文件。
六 处理证书
下面我们打开终端(位置:应用程序à实用工具à终端)。
cd到桌面,我们那三个文件所在的位置
1、把.cer的SSL证书转换为.pem文件,执行命令:
openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem
在桌面上会生成一个PushChatCert.pem文件
2、把私钥Push.p12文件转化为.pem文件:
openssl pkcs12 -nocerts -out PushChatKey.pem -in Push.p12
这里需要我们输入密码,这个密码也就是我们导出p12文件时的密码,也就是我们上面设置的abcabc。然后,需要我们对生成的pem文件设置一个密语,这里我们推荐还是用上面这个123456,防止混乱(当然你也可以设置成别的更有意义的密语),这里的密语是要告诉我们服务器的。这样,桌面上又会生成一个PushChatKey.pem文件
3、对生成的这两个pem文件再生成一个pem文件,来把证书和私钥整合到一个文件里:
cat PushChatCert.pem PushChatKey.pem > ck.pem
生成ck.pem文件
这样,我们的文件就制作完了。下面进入测试阶段
为了测试证书是否工作,执行下面的命令:
telnet gateway.sandbox.push.apple.com 2195
它将尝试发送一个规则的,不加密的连接到APNS服务。如果你看到上面的反馈,那说明你的MAC能够到达APNS。按下Ctrl+C关闭连接。如果得到一个错误信息,那么你需要确保你的防火墙允许2195端口。一般这里都不会出现什么问题。
下面我们要使用我们生成的SSL证书和私钥来设置一个安全的链接去链接苹果服务器:
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem
执行完这一句命令后需要我们输入密语
Enter pass phrase for PushChatKey.pem:
我们输入abcabc按回车
你会看到一个完整的输出,让你明白OpenSSL在后台做什么。如果链接是成功的,你可以随便输入一个字符,按下回车,服务器就会断开链接,如果建立连接时有问题,OpenSSL会给你返回一个错误信息。
当你在最后的时候你看到这样说明你已经成功了:
CONNECTED(00000003)
depth=1 /C=US/O=Entrust, Inc./OU=www.entrust.net/rpa isincorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust CertificationAuthority - L1C
verify error:num=20:unable to get local issuercertificate
verify return:0
---
Certificate chain
0s:/C=US/ST=California/L=Cupertino/O=Apple Inc./OU=iTMSEngineering/CN=gateway.sandbox.push.apple.com
i:/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated byreference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
1s:/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated byreference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
i:/O=Entrust.net/OU=www.entrust.net/CPS_2048incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.netCertification Authority (2048)
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFGzCCBAOgAwIBAgIETBz90jANBgkqhkiG9w0BAQUFADCBsTELMAkGA1UEBhMC
……省略……
fMGbLqkGn8YogdPqe5T1
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Cupertino/O=AppleInc./OU=iTMS Engineering/CN=gateway.sandbox.push.apple.com
issuer=/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa isincorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust CertificationAuthority - L1C
---
No client certificate CA names sent
---
SSL handshake has read 2731 bytes and written 2165 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES256-SHA
Session-ID:
Session-ID-ctx:
Master-Key:C7A47EED5E1F5……省略……369D4
Key-Arg : None
Start Time:1361862882
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
在这里提醒一下,也许你会看到像我这样的提示:verify error:num=20:unable to get local issuercertificate
verify return:0
其实是没问题的。
在Appdelegate.mm中添加
//推送 ,注册[[UIApplication sharedApplication] cancelAllLocalNotifications];if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]){//IOS8//创建UIUserNotificationSettings,并设置消息的显示类类型NSLog(@"IOS版本是8.0以上的");
// UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIRemoteNotificationTypeSound) categories:nil];
// [application registerUserNotificationSettings:notiSettings];[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];[application registerForRemoteNotifications];//本地推送[self addLocalNotification];application.applicationIconBadgeNumber -= 1;} else{ // ios7NSLog(@"IOS版本是8.0以下的");[application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert)];//本地推送[self addLocalNotification];application.applicationIconBadgeNumber -= 1;}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{NSLog(@"didReceiveRemoteNotification - (%@)", userInfo);// 진동!AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);//推送if (application.applicationState == UIApplicationStateActive) { //转为后台显示UILocalNotification *localNotification = [[UILocalNotification alloc] init];localNotification.userInfo = userInfo;localNotification.soundName = UILocalNotificationDefaultSoundName;localNotification.alertBody = [[userInfo objectForKey:@"aps"] objectForKey:@"alert"];localNotification.fireDate = [NSDate date];[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];}application.applicationIconBadgeNumber -= 1;
}
本地推送:
#pragma mark 添加本地通知
-(void)addLocalNotification{// NSDate *now = [NSDate date];
// //取得系统时间
// NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
// NSDateComponents *components = [[NSDateComponents alloc] init];
// NSInteger unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
// components = [calendar components:unitFlags fromDate:now];
// NSInteger hour = [components hour];
// NSInteger min = [components minute];
// NSInteger sec = [components second];
// NSInteger week = [components weekday];
// NSLog(@"现在是%ld:%ld:%ld,周%ld",hour,min,sec,week);
//
// NSDate *date = [NSDate date];
// NSTimeZone *zone = [NSTimeZone systemTimeZone];//修改时区
// NSInteger interval1 = [zone secondsFromGMTForDate: date];//修改时区
// NSDate *localDate1 = [date dateByAddingTimeInterval: interval1];//修改时区
// NSLog(@"今天%@", localDate1);
// //2),创建一个明天此时的日期(时间间隔是以秒为单位的)dateWithTimeIntervalSinceNow:
// NSDate *tomorrow = [NSDate dateWithTimeIntervalSinceNow:24 * 60 * 60];
// NSInteger interval2 = [zone secondsFromGMTForDate: tomorrow];
// NSDate *localDate2 = [tomorrow dateByAddingTimeInterval: interval2];
// NSLog(@"明天%@", localDate2);NSDateFormatter *formatter = [[NSDateFormatter alloc] init];[formatter setDateFormat:@"HH:mm:ss"];//定义本地通知对象UILocalNotification * notification=[[UILocalNotification alloc]init];//设置调用时间notification.timeZone=[NSTimeZone defaultTimeZone];//notification.fireDate=[NSDate dateWithTimeIntervalSinceNow:10.0];//通知触发的时间,10s以后//notification.fireDate= [NSDate dateWithTimeIntervalSince1970:11*60*60*24];//通知触发的时间,10s以后//notification.fireDate= localDate2;NSDate *date = [NSDate date];NSTimeZone *zone = [NSTimeZone systemTimeZone];//修改时区NSInteger interval1 = [zone secondsFromGMTForDate: date];//修改时区NSDate *localDate1 = [date dateByAddingTimeInterval: interval1];//修改时区localDate1 = [formatter dateFromString:@"10:00:00"];//通知发出的时间notification.fireDate = localDate1;NSLog(@"时间%@", notification.fireDate);notification.repeatInterval=kCFCalendarUnitWeekday;//通知重复次数notification.repeatCalendar=[NSCalendar currentCalendar];//当前日历,使用前最好设置时区等信息以便能够自动同步时间NSLog(@"日历%@", notification.repeatCalendar);//设置通知属性notification.alertBody=@"最近添加了诸多有趣的特性,是否立即体验?"; //通知主体notification.applicationIconBadgeNumber=1;//应用程序图标右上角显示的消息数notification.alertAction=@"打开应用"; //待机界面的滑动动作提示notification.alertLaunchImage=@"Default";//通过点击通知打开应用时的启动图片notification.soundName=UILocalNotificationDefaultSoundName;//收到通知时播放的声音,默认消息声音//notification.soundName=@"msg.caf";//通知声音(需要真机)//设置用户信息notification.userInfo=@{@"id":@1,@"user":@"Kenshin Cui"};//绑定到通知上的其他额外信息//调用通知[[UIApplication sharedApplication] scheduleLocalNotification:notification];[notification release];
}
远程推送:
- (void)application:(UIApplication *)applicationdid RegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {NSLog(@"regisger success:%@",pToken);//注册成功,将deviceToken保存到应用服务器数据库中NSLog(@"注册成功,将deviceToken保存到应用服务器数据库中");
}#pragma mark //调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{if (notificationSettings.types!=UIUserNotificationTypeNone) {[self addLocalNotification];}
}
-(void)addDeviceToken:(NSData *)deviceToken{NSString *key=@"DeviceToken";//[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];NSData *oldToken= [[NSUserDefaults standardUserDefaults]objectForKey:key];//如果偏好设置中的已存储设备令牌和新获取的令牌不同则存储新令牌并且发送给服务器端if (![oldToken isEqualToData:deviceToken]) {[[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:key];[self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];}//[self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];
}-(void)sendDeviceTokenWidthOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken{//注意一定确保真机可以正常访问下面的地址NSString *urlStr=@"http://192.168.1.1/test/setPush.php";urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL *url=[NSURL URLWithString:urlStr];NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];[requestM setHTTPMethod:@"POST"];NSString* str = [[[newToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];NSString *bodyStr=[NSString stringWithFormat:@"devicetoken=%@",str];NSData *body=[bodyStr dataUsingEncoding:NSUTF8StringEncoding];[requestM setHTTPBody:body];NSURLSession *session=[NSURLSession sharedSession];NSURLSessionDataTask *dataTask= [session dataTaskWithRequest:requestM completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {if (error) {NSLog(@"Send failure,error is :%@",error.localizedDescription);}else{NSLog(@"Send Success!");}}];[dataTask resume];
}
//- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification*)notification{
//
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"iWeibo" message:notification.alertBody delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
//
// [alert show];
//
// // 图标上的数字减1
//
// application.applicationIconBadgeNumber -= 1;
//
//}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{NSString* tokenString = [[[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];if ( tokenString != nil ){NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken - (%@)", tokenString);// [Todo] 발급받은 tokenString을 게임 서버를 통해 위미 푸시 서버로 전달합니다.// ~~~~[self addDeviceToken:deviceToken];}
}
下面我们把php服务器代码和生成的ck.pem文件放在统一文件夹下面(这里我们还是统一放在桌面上)。
用Xcode打开(其他工具也可以)php服务器端的代码,把deviceToken改成我们现在要进行测试的iphone的deviceToken(获得方法,:在Xcode的顶部工具栏点击windowàOrganizer,在左侧选中我们的iphone后,右边的Identifier后面的就是了),密语改成我们之前设置的abcabc。然后保存。
然后在终端运行命令(如果刚才你关闭了终端的话,最好ls一下,看看当前是不是在桌面),执行命令:
php pushMe.php
然后回车(pushMe为服务器文件名称)
如果出现这样的提示说明成功了,然后在iphone上,我们期待已久的推送消息终于来了。
最后贴上PHP的服务器的代码,这代码是从网上http://www.cocoachina.com/industry/20130321/5862.html拿来的,http://www.cocoachina.com/bbs/read.php?tid=102110&page=1 感谢大神无私的奉献。
<?php// Put your device token here (without spaces):
//$deviceToken = '577a952a3a08b1f90effee0c8fe45142e33e084d743e34068b6474f32c92cfb6'; //测试版本
//$deviceToken = '954ed011b0d64b7de1e16098c8394cd353a32f00a4968375510100ad5fe80b8f'; //发布版本
// Put your private key's passphrase here:密语
$passphrase = '密码';// Put your alert message here:
$info = mysql_connect("127.0.0.1","用户名","密码");//连接数据库
mysql_query("SET NAMES 'UTF8'");
if (!$info){die('Could not connect: ' . mysql_error());//连接失败显示的错误}
mysql_select_db("starfg", $info);
$pushList = mysql_query("select * from 表名");
while($row = mysql_fetch_array($pushList))//循环读取数据{echo $row['title'];//输出数据echo "<br />";$message = $row['title'];}$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);// Open a connection to the APNS server
//测试地址gateway.sandbox.push.apple.com:2195
//发布地址 gateway.push.apple.com:2195
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err,$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);if (!$fp)exit("Failed to connect: $err $errstr" . PHP_EOL);echo 'Connected to APNS' . PHP_EOL;
echo "<br />";// Create the payload body
$body['aps'] = array('alert' => $message,'sound' => 'default');// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
//$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
$activityList = mysql_query("select * from 表名");
while($row = mysql_fetch_array($activityList))//循环读取数据{echo $row['devicetoken'];//输出数据echo "<br />";$msg = chr(0) . pack('n', 32) . pack('H*', $row['devicetoken']) . pack('n', strlen($payload)) . $payload;// Send it to the server$result = fwrite($fp, $msg, strlen($msg));}// Send it to the server
//$result = fwrite($fp, $msg, strlen($msg));if (!$result)echo 'Message not delivered' . PHP_EOL;
elseecho 'Message successfully delivered' . PHP_EOL;// Close the connection to the server
fclose($fp);?>
IOS 本地推送和远程推送相关推荐
- iOS-本地推送和远程推送,常用的三方推送和常用的测试方法,推送实现和原理详解...
什么是消息推送 举一个常见的例子,我们的手机上经常会有弹出一些信息,例如QQ信息.微信信息等等,这就是常见的消息推送. 例如: 消息推送的类型: 在屏幕顶部显示一块横幅(显示具体内容) 在屏幕中间弹出 ...
- 本地推送通知和远程推送通知
推送通知 推送通知跟NSNotification有所区别: 1> NSNotification是抽象的,不可见的 2> 推送通知是可见的(能用肉眼看到) iOS中提供了2种推送通知: 本地 ...
- iOS应用处于前台、后台、应用被杀掉场景-收到远程推送内容进行收款语音播报;
iOS应用处于前台.后台.应用被杀掉场景-收到远程推送内容进行收款语音播报: 介绍: 收银应用两大技术点:远程推送.收款成功语音播报收款金额及其他附带语音内容: 顺便点下android语音播报有一个至 ...
- iOS 推送通知及推送扩展
概述 iOS中的通知包括本地推送通知和远程推送通知,两者在iOS系统中都可以通过弹出横幅的形式来提醒用户,点击横幅会打开应用.在iOS 10及之后版本的系统中,还支持通知扩展功能(UNNotifica ...
- iOS开发 - ANPs推送通知 标签: 推送通知ANPs远程推送、本地推送
iOS开发 - ANPs推送通知 标签: 推送通知ANPs远程推送本地推送 2015-05-03 14:12 3510人阅读 评论(0) 收藏 举报 本文章已收录于: iOS知识库 分类: [IO ...
- IOS开发之----远程推送通知
原文地址:IOS开发之----远程推送通知作者:倒計時 玩了一年的iPhone了各种App的远程通知接收了不少,每次接收到的时候,就在反思,这丫的怎么实现的! 由于工作方面一直没有接触的机会,所以只好 ...
- iOS远程推送原理及实现过程
该文章是我16年在公司博客上写的,除了证书注册的过程大致没有改变,像接收通知的方法都有所改变,所以将iOS 10 之后的接收通知及注册通知的方法在文章中补全,希望对正在处理远程推送的伙伴们有所帮助 一 ...
- git本地分支推送到远程分支
1.远端git库的创建和初始化 创建git仓库可以在远端创建一个仓库, 然后check到本地,在本地的文件里创建工程文件,然后提交 也可以将本地现有的工程和远端的空仓库关联 本地创建了一个工程 iOS ...
- 玩转ios友盟远程推送,16年5月图文防坑版
最近有个程序员妹子在做远程推送的时候遇到了困难,求助本帅.尽管本帅也是多彩的绘图工具,从没做过远程推送,但是本着互相帮助,共同进步的原则,本帅还是掩饰了自己的彩笔身份,耗时三天(休息时间)帮她完成了推 ...
最新文章
- 再见丑陋的 SwaggerUI,这款API文档生成神器界面更炫酷,逼格更高!
- pandas计算dataframe两列数据值相等的行号、取出DataFrame中两列值相等的行号
- 「译」JUnit 5 系列:基础入门
- linux od命令详解
- MySQL_控制台操作_01
- JSP标签JSTL(4)--URL
- 一个微软面试题--关于位结构体
- 图解!24 张图彻底弄懂九大常见数据结构!
- paip.备份导出ANDROID安卓自带记事本内容
- 文本挖掘学习笔记(二):文档信息向量化与主题关键词提取
- 循环不变式、数学归纳法、归纳推理和演绎推理学习总结
- 陈天出席华盛顿大学春季招聘会 | ArcBlock 动态
- 产品经理入门攻略(3岁的PM成长分享)
- ssm电子族谱信息管理系统的设计与实现毕业设计-附源码161714
- 面试官:Java8 lambda 表达式 forEach 如何提前终止?
- Markdown设置字体大小、颜色、类型、加粗
- android textview基线,关于Textview基准线的计算
- symbian学习转载
- Sketch(三)——手绘微信图标
- 东莞面包车狂奔伤多人 10余警车围堵开枪撞停