iOS远程推送Demo和PHP服务器配置、以及问题的解决方法

写这篇文章的目的是为了自己做笔记,以免下次再出现同样的错误,如果对你有帮助,你可以接着往下看看。。。。

1、配置APNs相关证书

     关于相关证书的配置,参考的是网上的博客:http://zxs19861202.iteye.com/blog/1532460。其中公钥和私钥的文件的p12导出一定要导正确,不然在服务器发送时会报错,楼主就犯了这个错误。这块会在后面提及。

     特别注意的是,在开发者帐号中创建的APP ID必须与项目中 的Bundle Identifier标识一致才能接收服务器发出的推送!!!!!

     然后创建一个Demo,用来测试远程推送,其主要实在AppDelegate.m中加入以下代码:

#define push_server @"http://localhost/simplepush/simplepush.php"

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

@interface AppDelegate ()


@end


@implementation AppDelegate


//    注册推送通知功能

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    //判断系统版本,然后进行通知配置,因为ios8以后skd换了

    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

        

    }else{

        //1.创建消息上面要添加的动作(按钮的形式显示出来)

        UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];

        action.identifier = @"action";//按钮的标示

        action.title=@"Accept";//按钮的标题

        action.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序

        //    action.authenticationRequired = YES;

        //    action.destructive = YES;

        

        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];

        action2.identifier = @"action2";

        action2.title=@"Reject";

        action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理

        action.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略;

        action.destructive = YES;

        

        //2.创建动作(按钮)的类别集合

        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];

        categorys.identifier = @"alert";//这组动作的唯一标示,推送通知的时候也是根据这个来区分

        [categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];

        

        //3.创建UIUserNotificationSettings,并设置消息的显示类类型

        UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys, nil]];

        [application registerUserNotificationSettings:notiSettings];

        

    }

    return YES;

}


- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

{

    

    //注册远程通知,ios8以上的注册

    [application registerForRemoteNotifications];

}


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {

    //设备号

    NSLog(@"regisger success:%@", pToken);

    

    //注册成功,将deviceToken保存到应用服务器数据库中

    

}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{

    

    //接收到通知

    NSDictionary *info = [userInfo objectForKey:@"aps"];

    // 处理推送消息

    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"通知" message:info[@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];

    [alert show];

    NSLog(@"%@", userInfo);

}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

    NSLog(@"Regist fail%@",error);

    

    //通知错误时候代理

}

#define push_server @"http://localhost/simplepush/simplepush.php"

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

@interface AppDelegate ()


@end


@implementation AppDelegate


//    注册推送通知功能

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    //判断系统版本,然后进行通知配置,因为ios8以后skd换了

    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

        

    }else{

        //1.创建消息上面要添加的动作(按钮的形式显示出来)

        UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];

        action.identifier = @"action";//按钮的标示

        action.title=@"Accept";//按钮的标题

        action.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序

        //    action.authenticationRequired = YES;

        //    action.destructive = YES;

        

        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];

        action2.identifier = @"action2";

        action2.title=@"Reject";

        action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理

        action.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略;

        action.destructive = YES;

        

        //2.创建动作(按钮)的类别集合

        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];

        categorys.identifier = @"alert";//这组动作的唯一标示,推送通知的时候也是根据这个来区分

        [categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];

        

        //3.创建UIUserNotificationSettings,并设置消息的显示类类型

        UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys, nil]];

        [application registerUserNotificationSettings:notiSettings];

        

    }

    return YES;

}


- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

{

    

    //注册远程通知,ios8以上的注册

    [application registerForRemoteNotifications];

}


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {

    //设备号

    NSLog(@"regisger success:%@", pToken);

    

    //注册成功,将deviceToken保存到应用服务器数据库中

    

}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{

    

    //接收到通知

    NSDictionary *info = [userInfo objectForKey:@"aps"];

    // 处理推送消息

    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"通知" message:info[@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];

    [alert show];

    NSLog(@"%@", userInfo);

}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

    NSLog(@"Regist fail%@",error);

    

    //通知错误时候代理

}

#define push_server @"http://localhost/simplepush/simplepush.php"

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

@interface AppDelegate ()


@end


@implementation AppDelegate


//    注册推送通知功能

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    //判断系统版本,然后进行通知配置,因为ios8以后skd换了

    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

        

    }else{

        //1.创建消息上面要添加的动作(按钮的形式显示出来)

        UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];

        action.identifier = @"action";//按钮的标示

        action.title=@"Accept";//按钮的标题

        action.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序

        //    action.authenticationRequired = YES;

        //    action.destructive = YES;

        

        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];

        action2.identifier = @"action2";

        action2.title=@"Reject";

        action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理

        action.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略;

        action.destructive = YES;

        

        //2.创建动作(按钮)的类别集合

        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];

        categorys.identifier = @"alert";//这组动作的唯一标示,推送通知的时候也是根据这个来区分

        [categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];

        

        //3.创建UIUserNotificationSettings,并设置消息的显示类类型

        UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys, nil]];

        [application registerUserNotificationSettings:notiSettings];

        

    }

    return YES;

}


- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

{

    

    //注册远程通知,ios8以上的注册

    [application registerForRemoteNotifications];

}


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {

    //设备号

    NSLog(@"regisger success:%@", pToken);

    

    //注册成功,将deviceToken保存到应用服务器数据库中

    

}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{

    

    //接收到通知

    NSDictionary *info = [userInfo objectForKey:@"aps"];

    // 处理推送消息

    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"通知" message:info[@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];

    [alert show];

    NSLog(@"%@", userInfo);

}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

    NSLog(@"Regist fail%@",error);

    

    //通知错误时候代理

}

#define push_server @"http://localhost/simplepush/simplepush.php"

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

@interface AppDelegate ()


@end


@implementation AppDelegate


//    注册推送通知功能

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    //判断系统版本,然后进行通知配置,因为ios8以后skd换了

    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

        

    }else{

        //1.创建消息上面要添加的动作(按钮的形式显示出来)

        UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];

        action.identifier = @"action";//按钮的标示

        action.title=@"Accept";//按钮的标题

        action.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序

        //    action.authenticationRequired = YES;

        //    action.destructive = YES;

        

        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];

        action2.identifier = @"action2";

        action2.title=@"Reject";

        action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理

        action.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略;

        action.destructive = YES;

        

        //2.创建动作(按钮)的类别集合

        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];

        categorys.identifier = @"alert";//这组动作的唯一标示,推送通知的时候也是根据这个来区分

        [categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];

        

        //3.创建UIUserNotificationSettings,并设置消息的显示类类型

        UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys, nil]];

        [application registerUserNotificationSettings:notiSettings];

        

    }

    return YES;

}


- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

{

    

    //注册远程通知,ios8以上的注册

    [application registerForRemoteNotifications];

}


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {

    //设备号

    NSLog(@"regisger success:%@", pToken);

    

    //注册成功,将deviceToken保存到应用服务器数据库中

    

}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{

    

    //接收到通知

    NSDictionary *info = [userInfo objectForKey:@"aps"];

    // 处理推送消息

    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"通知" message:info[@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];

    [alert show];

    NSLog(@"%@", userInfo);

}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

    NSLog(@"Regist fail%@",error);

    

    //通知错误时候代理

}

#define push_server @"http://localhost/simplepush/simplepush.php"

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

@interface AppDelegate ()


@end


@implementation AppDelegate


//    注册推送通知功能

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    //判断系统版本,然后进行通知配置,因为ios8以后skd换了

    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

        

    }else{

        //1.创建消息上面要添加的动作(按钮的形式显示出来)

        UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];

        action.identifier = @"action";//按钮的标示

        action.title=@"Accept";//按钮的标题

        action.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序

        //    action.authenticationRequired = YES;

        //    action.destructive = YES;

        

        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];

        action2.identifier = @"action2";

        action2.title=@"Reject";

        action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理

        action.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略;

        action.destructive = YES;

        

        //2.创建动作(按钮)的类别集合

        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];

        categorys.identifier = @"alert";//这组动作的唯一标示,推送通知的时候也是根据这个来区分

        [categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];

        

        //3.创建UIUserNotificationSettings,并设置消息的显示类类型

        UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys, nil]];

        [application registerUserNotificationSettings:notiSettings];

        

    }

    return YES;

}


- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

{

    

    //注册远程通知,ios8以上的注册

    [application registerForRemoteNotifications];

}


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {

    //设备号

    NSLog(@"regisger success:%@", pToken);

    

    //注册成功,将deviceToken保存到应用服务器数据库中

    

}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{

    

    //接收到通知

    NSDictionary *info = [userInfo objectForKey:@"aps"];

    // 处理推送消息

    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"通知" message:info[@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];

    [alert show];

    NSLog(@"%@", userInfo);

}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

    NSLog(@"Regist fail%@",error);

    

    //通知错误时候代理

}

代码写好了,我们还需要在target ->Capabilities ->打开Push Notification,如图:

  iOS远程推送Demo和PHP服务器配置、以及问题的解决方法_第1张图片

最后在真机上运行程序,会获得真机的token。


2、配置服务器

     至于配置服务器,首先需要在mac的终端启动Apache和PHP,由于楼主不懂PHP,所以借鉴这篇文章:http://www.tuicool.com/articles/ZjmqYb3

    原本我在没搭建简单的PHP服务器的情况下,以为会很难,不过确实很难,难在我不懂PHP啊!所以不停的查资料看看怎么搭建,以下是楼主的搭建过程(基于PHP已经开启的状态):

    1)Finder -> 前往文件夹 -> 输入: /Library/WebServer/Documents/ -> 前往。跳转到服务器文件夹,在里面创建一个文件夹simple push -> 将之前公钥和私钥合成的pem文件复制到这个文件夹 -> 创建一个PHP文件simplepush.php,其代码主要为:

set_time_limit(0);

sleep(5);

// 这里是我们上面得到的deviceToken,直接复制过来(记得去掉空格)

$deviceToken = 'da7a0ac7db89f6707d67627d5702673c8fa3d666b0c52ba793159351bc9f4a9d';


// Put your private key's passphrase here:

$passphrase = '123456';


// Put your alert message here:

$message = 'My first push test!';


////////////////////////////////////////////////////////////////////////////////


$ctx = stream_context_create();

stream_context_set_option($ctx, 'ssl', 'allow_self_signed', true);

stream_context_set_option($ctx, 'ssl', 'verify_peer', false);

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);

//$fp=stream_socket_client("udp://127.0.0.1:1113", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $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,

'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;


// 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);

?>


将上面代码复制粘贴到simplepush.php中,只需要改动其中三个地方:

       第一个是:deviceToken,将1中获取的token复制过来

       第二个是:passphrase,是在合成私钥时输入的密码

       第三个是:local_cert后面的ck.pem,改成自己合成的公钥和私钥pem的合成文件xx.pem。


最后在浏览器输入:http://localhost/simplepush/simplepush.php

如果显示为:Connected to APNS Message successfully delivered则已经推送成功了。手机将收到推送,如图

iOS远程推送Demo和PHP服务器配置、以及问题的解决方法_第2张图片


3、楼主在运行时出现的各种问题以及解决方案

主要遇到了两个大问题:

 第一个是PHP服务器的,我在浏览器上输入http://localhost/simplepush/simplepush.php运行服务器时报错:

Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure in /Library/WebServer/Documents/simplepush/simplepush.php on line 123


Warning: stream_socket_client(): Failed to enable crypto in /Library/WebServer/Documents/simplepush/simplepush.php on line 123


Warning: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in /Library/WebServer/Documents/simplepush/simplepush.php on line 123

Failed to connect: 0



如果是这个错误,一般需要检查你合成的证书有没有错!楼主就是在导出公钥时出错了。。。最后的解决方案是:

导出私钥Key.p12 在终端输入命令 openssl pkcs12 -nocerts -out Key.pem -in Key.p12,然后将aps_development.cer转换成p12.命令为:openssl x509 -in aps_development.cer -inform der -out Cert.pem,最后再将Key.pem和Cert.pem合成ck.pem。再次运行服务器就可以啦!



第二个问题是关于ID的问题,由于证书的App ID必须与Bundle Identifier一致,但楼主傻了,去target -> General -> Team添加了开发者帐号,导致identifier是一致的,但是老是提示楼主这个ID不可用,需要重新创建。

 这个问题困扰了楼主好几天。。。一直没想通。。看了好多前辈的方案什么的,,还是不行。。。今天突然灵机一动,重新创建了一个程序,没加入Team之前在Capabilites里面会有Push Notification这个选项。。于是我就打开它,运行程序,,就可以了。。真的就可以了。。当时真的懵了、、不过能运行起来就很开心了


你可能感兴趣的:(iOS开发)