我是靠谱客的博主 斯文帽子,最近开发中收集的这篇文章主要介绍APNs消息推送,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

什么是APNs消息推送

由APNs服务器、ProviderService、iOS系统、App构成的通讯系统,是移动互联网与传统的Web最明显的区别的地方。正因为有了推送,实现了服务端能够反向与用户建立联系,而不是等待用户访问Web服务器。

APNs: Apple Push Notification Service的缩写,是苹果的服务器。

推送流程

分为两阶段:
阶段一
1.app向APNs注册该device。
2. APNs返回device token给app。
3. app将device token 传给app服务端,由服务端保存。

Created with Raphaël 2.1.0 app app APNs APNs app service app service 1.注册 2.device token 3.device token

阶段二
1.device和APNs建立长连接。
2.app服务端将device token + 消息打包发给APNs。
3.APNs将消息发送给divice。

Created with Raphaël 2.1.0 app app APNs APNs app service app service 1.长连接 2.device token + 消息 3.消息

客户端

注册‘通知设置’,以允许你的应用在设备上开启通知

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    return YES;
}

收到‘通知设置’的回调,给你的设备‘注册远程通知’

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    if (notificationSettings.types != UIUserNotificationTypeNone) {
        [application registerForRemoteNotifications];
    } else {

    }
}

收到‘注册远程通知’的回调,成功就将device token发到app服务端

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [self sendToProviderWithDiviceToken:deviceToken];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"#error in registration:%@", error);
}

发送device token 的代码如下,这边将设备的其他信息一起发送

- (void)sendToProviderWithDiviceToken:(NSData*)deviceToken {
    NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
    NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    UIDevice *dev = [UIDevice currentDevice];
    NSString *deviceName = dev.name;
    NSString *deviceModel = dev.model;
    NSString *deviceSystemVersion = dev.systemVersion;
    NSString *pushBadge = @"enabled";
    NSString *pushAlert = @"enabled";
    NSString *pushSound = @"enabled";

    NSString *resultToken = [[[[deviceToken description]
                               stringByReplacingOccurrencesOfString:@"<"withString:@""]
                              stringByReplacingOccurrencesOfString:@">" withString:@""]
                             stringByReplacingOccurrencesOfString: @" " withString: @""];

    NSString *host = @"deeepthinking.com";
    NSString *path = [NSString stringWithFormat:@"/push/apns.php?task=%@&appname=%@&appversion=%@&devicetoken=%@&devicename=%@&devicemodel=%@&deviceversion=%@&pushbadge=%@&pushalert=%@&pushsound=%@", @"register", appName, appVersion, resultToken, deviceName, deviceModel, deviceSystemVersion, pushBadge, pushAlert, pushSound];
    NSURL *url = [[NSURL alloc] initWithScheme:@"http" host:host path:[path  stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    NSLog(@"#request:%@", url);
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *urlR, NSData *returnData, NSError *e) {
                               NSLog(@"#response:%@", [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding]);
                           }];

}

第二阶段app服务器发送信息到APNs,然后你的设备就会收到一条推送,点开推送,会回调以下方法

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
    NSDictionary *apsInfo = [userInfo objectForKey:@"aps"];

    NSString *alert = [apsInfo objectForKey:@"alert"];
    NSLog(@"Received Push Alert: %@", alert);

    NSString *sound = [apsInfo objectForKey:@"sound"];
    NSLog(@"Received Push Sound: %@", sound);

    NSString *badge = [apsInfo objectForKey:@"badge"];
    NSLog(@"Received Push Badge: %@", badge);
    application.applicationIconBadgeNumber = [[apsInfo objectForKey:@"badge"] integerValue];
}

通知

iOS操作系统的通知包括了App的通知、系统的通知和官方应用的通知,实质上就是推送的数据在iOS操作系统上的表现和本地通知在iOS操作系统的表现,在交互上iOS10的通知大大增强,可定制化UI,增加了更加细分的通知权限管理和更多的通知设定,例如远程通知、时间通知、地理位置通知和日历通知。
很多开发者都知道iOS10中苹果升级推出了 User Notifications Framework与 User Notifications UI Framework两个框架,但是千万不要跟推送混为一谈,这两个框架升级和打包的是通知的功能增加和通知交互层面上的改进。

推送Push只不过是iOS10通知的一种触发器。

服务端

服务端接收到device token 后就保存到数据库,等有需要的时候就拿出来和推送信息发送到APNs。

可以参考下面的开源代码:
easyapns
ApnsPHP

发送信息到APNs的代码大致如下:

<?php  
// 这里是我们上面得到的deviceToken
$deviceToken=$_POST['deviceToken'];  

// Put your private key's passphrase here:  
$passphrase = 'password';  

// Put your alert message here:  
$message = $_POST['message'];  

$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  
//这个为正是的发布地址  
//$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);  
//这个是沙盒测试地址,发布到appstore后记得修改哦    
$fp = stream_socket_client('ssl://gateway.sandbox.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;  
// Create the payload body  

$body['aps'] = array(  
'alert' => $message,  
'forum_id' => 88,  
'topic_id' => 999,  
);  

// Encode the payload as JSON  
$payload = json_encode($body);  

// Build the binary notification   
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;  

// Send it to the server  
$result = fwrite($fp, $msg, strlen($msg));  

if (!$result){  
    echo 'Message not delivered' . PHP_EOL;  
}else{  
    echo 'Message successfully delivered' . PHP_EOL;  
}  

// Close the connection to the server  
fclose($fp);  

?>  

证书

服务端和APNs要顺利通讯,还需要配置证书(上面代码中的ck.pem,passphrase是证书的密码),证书生成过程如下:
1.在你的开发者账号上生成你app的推送证书aps_development .cer(开发用开发证书,发布用发布证书)
2.安装推送证书到mac的钥匙串管理器,导出证书的秘钥文件(下拉列表中钥匙形状的那个)为push_key.p12
3.将aps_development .cer转成.pem文件,执行命令:

openssl x509 -in aps_development.cer -inform der -out push_cer.pem

4.将push_key.p12也转成.pem,执行命令:

openssl pkcs12 -nocerts -out push_key.pem -in push_key.p12

期间要输入密码,这个密码就是上面代码中要用的passphrase

5.将push_cer.pem和push_key.pem合成一个,即ck.pem

cat push_cer.pem push_key.pem > ck.pem

APNs地址

测试地址:gateway.sandbox.push.apple.com:2195
发布地址:gateway.push.apple.com:2195
测试的地址用的是沙盒,发布地址是不同的。发布软件的时候记得改过来

发送到APNs的信息格式

json包中除了alert,badge,sound之外,还是是可以自定值的。如:

{
  "aps" :
           { "alert" :
                      {
                        "action-loc-key" : "显示" ,
                        "body" : "This is the alert text"
                       },
             "badge" : 1,
             "sound" : "default" },
  "server" : { "serverId" : 1, "name" : "Server name"}
  }

以前只支持发送256字节,多了APNs不收,现在支持发送4k。

大批量发送

将所有deviceToken在mysql查询出来foreach存入Redis中(包括推送内容),然后编写一个php从Redis中取数据并发送的脚本(每次都建立苹果连接),php脚本每次开启运行5分钟、5分钟以后自动退出。然后编辑个shell脚本,循环执行php脚本20次(次数根据deviceToken多少进行增加),最后将shell脚本编辑到crontab中运行、至于运行时间设置为php脚本执行时间最好、这样就相当于不间断取数据推送。这样做的好处就是预防php超时。

feedback

APNS的feedback是一个非常贴心的服务,他会告诉你近期推送的消息,有哪些设备由于卸载了应用而无法在通知中显示消息。

那么,我们通过定期从feedback中获得这些devicetoken后,在数据库中进行标记,在下次的推送中,从消息队列中剔除这些devicetoken,这样减少了无用功,推送一次会完成的更快。

参考文档:

APNs Overview
一步一步教你做ios推送
苹果推送APNS自己总结
用php搭建apns推送服务器
php实现推送ios消息大数据量的实例
iOS10里的通知与推送
php从苹果apns的feedback服务器获取推送失败的token

最后

以上就是斯文帽子为你收集整理的APNs消息推送的全部内容,希望文章能够帮你解决APNs消息推送所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(68)

评论列表共有 0 条评论

立即
投稿
返回
顶部