• 远程服务器推送


    本文为博主原创文章,未经博主允许不得转载。

    目录(?)[+]

    一、背景概述:

    1,环境配置

    APNS:Apple Push Notification Service。本文对推送相关概念不再赘述,只侧重完整流程。 

    Demo 开发环境:Mac os 10.9.4  ,Xcode 6.0.1 ;测试设备:iphone 4s(ios 7.1)

    服务端开发环境:mac 10.9.4  + php 5.4.24、

    Demo 下载地址:点击打开链接

    2,APNS 相关博客

    如对apns相关概念不清楚,可参考以下几个博客:(博客中部分内容重复,但总体来说,通读一遍,还是大有裨益的)

     http://cshbbrain.iteye.com/blog/1859810  =》IOS 基于APNS消息推送原理与实现(JAVA后台)

    http://www.cnblogs.com/qq78292959/archive/2012/07/16/2593651.html   =》iOS消息推送机制的实现

    http://blog.csdn.net/xunyn/article/details/8243573  =》APNS编程----iOS真机测试消息推送

    http://blog.csdn.net/wswqiang/article/details/8208581  =》IOS APNS 处理

    http://eric-gao.iteye.com/blog/1567777  =》 IOS PEM 文件的生成

    http://www.36coder.com/study/996.html  =》PHP 实现APNS 推送

    http://blog.csdn.net/sxfcct/article/details/7939082  =》 APNS 相关总结(推荐)

    3,APNS 接口

    消息推送:

    开发接口:gateway.sandbox.push.apple.com:2195

    发布接口:gateway.push.apple.com:2195

    反馈服务:

    开发接口:feedback.sandbox.push.apple.com:2196

    发布接口:产品接口:feedback.push.apple.com:2196

    二、制作Push证书和Pem文件

    1,新建一个App ID

    新建流程不再赘述,这里只提醒两点:1》App ID Suffix 中,一定要选择Explicit App ID;2》App Services 中,记得勾选Push Notifications。这里以新建一个id为:com.eversoft.PushDemo 为例。

    2,配置push开发证书

    在App IDs中,选中刚才新建的App id:com.eversoft.PushDemo ,单击,展开详细信息属性。
    在详细信息属性中,单击下方的“Edit”按钮,
    在新打开的编辑界面,单击“Create Certificate”,
    在新打开的界面中,会提示我们,创建一个csr 证书签名请求文件。具体的创建步骤,界面中已经给出了详细的英文说明。
    在进行下一步之前,我们先按照英文说明,创建一个 CSR 文件。
    • 在mac电脑上,打开应用程序  keychain(钥匙串访问);
    • 在keychain菜单栏中,依次选择“钥匙串访问”=》“证书助理”=》“从证书颁发机构请求证书”;
    • 在新打开的“证书助理”界面中,填写用户电子邮件地址,常用名称,CA电子邮件地址,这两个邮件地址直接填写你的苹果账号的邮件地址即可,然后选择“存储到磁盘”,然后点击“继续”;
    • 选择CSR文件保存位置,“存储”即可。至此, CSR 文件,制作完成。
    回到刚才我们的web页面上,点击“Continue”,进入下一页面;新的页面中,会要求我们上传刚才制作的csr文件,选择“Choose File”,找到我们刚才存储的csr文件,单击“打开”,最后,点击页面上的“Generate”按钮,到此,开发使用的push证书制作完毕。
    证书生成成功后,选择“Download”,将制作好的证书下载到本地。然后双击下载的证书aps_development.cer,双击后,证书就自动导入到钥匙串中了。
     
    打开 keychain,左侧钥匙串选择“登录”,种类选择“所有项目”,在右侧窗口中,选中刚才导入的Apple Development IOS Push Services证书(不用选中专用密钥),右键,选择导出,命名为:ck.p12 ,存储时,会提示输入保护密码,这里为演示方便,就输入了123456。之后又会要求输入电脑登录密码,输入即可。

    3,生成PEM文件

    最后,打开终端,执行以下命令,生成pem文件

    openssl pkcs12 -in ck.p12 -out ck.pem -nodes 

    执行时,会要求输入导入密码,这里输入刚才的保护密码123456即可。

    到此,php 服务端使用的pem证书就制作完毕了。

    Development PP 文件制作不再赘述。

    三、IOS 代码编写

    首先,在AppDelegate.m 中:

    1,注册通知

    1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
    2.     // Override point for customization after application launch.  
    3.     ViewController *mainCtrl=[[ViewController alloc] init];  
    4.     self.window.rootViewController=mainCtrl;  
    5.       
    6.     //注册通知  
    7.     if ([UIDevice currentDevice].systemVersion.doubleValue<8.0) {  
    8.         [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge)];  
    9.     }  
    10.     else {  
    11.         [[UIApplication sharedApplication] registerForRemoteNotifications];  
    12.         [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil]];  
    13.     }  
    14.       
    15.     //判断是否由远程消息通知触发应用程序启动  
    16.     if (launchOptions) {  
    17.         //获取应用程序消息通知标记数(即小红圈中的数字)  
    18.         NSInteger badge = [UIApplication sharedApplication].applicationIconBadgeNumber;  
    19.         if (badge>0) {  
    20.             //如果应用程序消息通知标记数(即小红圈中的数字)大于0,清除标记。  
    21.             badge--;  
    22.             //清除标记。清除小红圈中数字,小红圈中数字为0,小红圈才会消除。  
    23.             [UIApplication sharedApplication].applicationIconBadgeNumber = badge;  
    24.             NSDictionary *pushInfo = [launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"];  
    25.               
    26.             //获取推送详情  
    27.             NSString *pushString = [NSString stringWithFormat:@"%@",[pushInfo  objectForKey:@"aps"]];  
    28.             UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"finish Loaunch" message:pushString delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:nil, nil nil];  
    29.             [alert show];  
    30.         }  
    31.     }  
    32.       
    33.     return YES;  
    34. }  

    2,注册通知后,获取device token

    1. - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {  
    2.     NSString *token = [NSString stringWithFormat:@"%@", deviceToken];  
    3.     NSLog(@"My token is:%@", token);  
    4.     //这里应将device token发送到服务器端  
    5. }  
    6.   
    7. - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {  
    8.     NSString *error_str = [NSString stringWithFormat: @"%@", error];  
    9.     NSLog(@"Failed to get token, error:%@", error_str);  
    10. }  

    3,接收推送通知

    1. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo  
    2. {  
    3.     [UIApplication sharedApplication].applicationIconBadgeNumber=0;  
    4.     for (id key in userInfo) {  
    5.         NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);  
    6.     }  
    7.     /* eg. 
    8.     key: aps, value: { 
    9.         alert = "U8fd9U662fU4e00U6761U6d4bU8bd5U4fe1U606f"; 
    10.         badge = 1; 
    11.         sound = default; 
    12.     } 
    13.      */  
    14.     UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"remote notification" message:userInfo[@"aps"][@"alert"] delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:nil, nil nil];  
    15.     [alert show];  
    16. }  

    注意:app 前台运行时,会调用 remote notification;app后台运行时,点击提醒框,会调用remote notification,点击app 图标,不调用remote notification,没反应;app 没有运行时,点击提醒框,finishLaunching   中,launchOptions 传参,点击app 图标,launchOptions 不传参,不调用remote notification。

    四、服务器端代码编写

    此章不在IOS程序员职责范围之内,故只给出示例代码,不做深入讨论。

    1,php 源码:

    1. <!DOCTYPE html>  
    2. <html>  
    3. <head>  
    4. <meta http-equiv="content-type" content="text/html;charset=utf-8">  
    5. <title>APNS</title>  
    6. </head>  
    7. <body>  
    8. <?php  
    9. /** 
    10. * @file apns.php 
    11. * @synopsis  apple APNS class 
    12. * @author Yee, <rlk002@gmail.com
    13. * @version 1.0 
    14. * @date 2012-09-17 11:27:59 
    15. */  
    16.     class APNS  
    17.     {  
    18.         const ENVIRONMENT_PRODUCTION = 0;  
    19.         const ENVIRONMENT_SANDBOX = 1;  
    20.         const DEVICE_BINARY_SIZE = 32;  
    21.         const CONNECT_RETRY_INTERVAL = 1000000;  
    22.         const SOCKET_SELECT_TIMEOUT = 1000000;  
    23.         const COMMAND_PUSH = 1;  
    24.         const STATUS_CODE_INTERNAL_ERROR = 999;  
    25.         const ERROR_RESPONSE_SIZE = 6;  
    26.         const ERROR_RESPONSE_COMMAND = 8;  
    27.         const PAYLOAD_MAXIMUM_SIZE = 256;  
    28.         const APPLE_RESERVED_NAMESPACE = 'aps';  
    29.         protected $_environment;  
    30.         protected $_providerCertificateFile;  
    31.         protected $_rootCertificationAuthorityFile;  
    32.         protected $_connectTimeout;  
    33.         protected $_connectRetryTimes = 3;  
    34.         protected $_connectRetryInterval;  
    35.         protected $_socketSelectTimeout;  
    36.         protected $_hSocket;  
    37.         protected $_deviceTokens = array();  
    38.         protected $_text;  
    39.         protected $_badge;  
    40.         protected $_sound;  
    41.         protected $_customProperties;  
    42.         protected $_expiryValue = 604800;  
    43.         protected $_customIdentifier;  
    44.         protected $_autoAdjustLongPayload = true;  
    45.         protected $asurls = array('ssl://gateway.push.apple.com:2195','ssl://gateway.sandbox.push.apple.com:2195');  
    46.         protected $_errorResponseMessages = array  
    47.                             (  
    48.                                 0   => 'No errors encountered',  
    49.                                 1 => 'Processing error',  
    50.                                 2 => 'Missing device token',  
    51.                                 3 => 'Missing topic',  
    52.                                 4 => 'Missing payload',  
    53.                                 5 => 'Invalid token size',  
    54.                                 6 => 'Invalid topic size',  
    55.                                 7 => 'Invalid payload size',  
    56.                                 8 => 'Invalid token',  
    57.                                 self::STATUS_CODE_INTERNAL_ERROR => 'Internal error'  
    58.                             );  
    59.           
    60.         function __construct($environment,$providerCertificateFile)  
    61.         {  
    62.             if($environment != self::ENVIRONMENT_PRODUCTION && $environment != self::ENVIRONMENT_SANDBOX)   
    63.             {  
    64.                 throw new Exception(  
    65.                     "Invalid environment '{$environment}'"  
    66.                 );  
    67.             }  
    68.             $this->_environment = $environment;  
    69.   
    70.             if(!is_readable($providerCertificateFile))   
    71.             {  
    72.                 throw new Exception(  
    73.                     "Unable to read certificate file '{$providerCertificateFile}'"  
    74.                 );  
    75.             }  
    76.             $this->_providerCertificateFile = $providerCertificateFile;  
    77.   
    78.             $this->_connectTimeout = @ini_get("default_socket_timeout");  
    79.             $this->_connectRetryInterval = self::CONNECT_RETRY_INTERVAL;  
    80.             $this->_socketSelectTimeout = self::SOCKET_SELECT_TIMEOUT;  
    81.         }  
    82.   
    83.         public function setRCA($rootCertificationAuthorityFile)  
    84.         {  
    85.             if(!is_readable($rootCertificationAuthorityFile))   
    86.             {  
    87.                 throw new Exception(  
    88.                     "Unable to read Certificate Authority file '{$rootCertificationAuthorityFile}'"  
    89.                 );  
    90.             }  
    91.             $this->_rootCertificationAuthorityFile = $rootCertificationAuthorityFile;  
    92.         }  
    93.   
    94.         public function getRCA()  
    95.         {  
    96.             return $this->_rootCertificationAuthorityFile;  
    97.         }  
    98.   
    99.         protected function _connect()  
    100.         {  
    101.             $sURL = $this->asurls[$this->_environment];  
    102.             $streamContext = stream_context_create(  
    103.                 array  
    104.                     (  
    105.                         'ssl' => array  
    106.                         (  
    107.                             'verify_peer' => isset($this->_rootCertificationAuthorityFile),  
    108.                             'cafile' => $this->_rootCertificationAuthorityFile,  
    109.                             'local_cert' => $this->_providerCertificateFile  
    110.                         )  
    111.                     )  
    112.                 );  
    113.   
    114.             $this->_hSocket = @stream_socket_client($sURL,$nError,$sError,$this->_connectTimeout,STREAM_CLIENT_CONNECT, $streamContext);  
    115.   
    116.             if (!$this->_hSocket)   
    117.             {  
    118.                 throw new Exception  
    119.                 (  
    120.                     "Unable to connect to '{$sURL}': {$sError} ({$nError})"  
    121.                 );  
    122.             }  
    123.             stream_set_blocking($this->_hSocket, 0);  
    124.             stream_set_write_buffer($this->_hSocket, 0);  
    125.             return true;  
    126.         }  
    127.   
    128.         public function connect()  
    129.         {  
    130.             $bConnected = false;  
    131.             $retry = 0;  
    132.             while(!$bConnected)   
    133.             {  
    134.                 try   
    135.                 {  
    136.                     $bConnected = $this->_connect();  
    137.                 }catch (Exception $e)   
    138.                 {  
    139.                     if ($nRetry >= $this->_connectRetryTimes)   
    140.                     {  
    141.                         throw $e;  
    142.                     }else   
    143.                     {  
    144.                         usleep($this->_nConnectRetryInterval);  
    145.                     }  
    146.                 }  
    147.                 $retry++;  
    148.             }  
    149.         }  
    150.   
    151.         public function disconnect()  
    152.         {  
    153.             if (is_resource($this->_hSocket))   
    154.             {  
    155.                 return fclose($this->_hSocket);  
    156.             }  
    157.             return false;  
    158.         }  
    159.   
    160.         protected function getBinaryNotification($deviceToken, $payload, $messageID = 0, $Expire = 604800)  
    161.         {  
    162.             $tokenLength = strlen($deviceToken);  
    163.             $payloadLength = strlen($payload);  
    164.   
    165.             $ret  = pack('CNNnH*', self::COMMAND_PUSH, $messageID, $Expire > 0 ? time() + $Expire : 0, self::DEVICE_BINARY_SIZE, $deviceToken);  
    166.             $ret .= pack('n', $payloadLength);  
    167.             $ret .= $payload;  
    168.             return $ret;  
    169.         }  
    170.   
    171.         protected function readErrorMessage()  
    172.         {  
    173.             $errorResponse = @fread($this->_hSocket, self::ERROR_RESPONSE_SIZE);  
    174.             if ($errorResponse === false || strlen($errorResponse) != self::ERROR_RESPONSE_SIZE)   
    175.             {  
    176.                 return;  
    177.             }  
    178.             $errorResponse = $this->parseErrorMessage($errorResponse);  
    179.             if (!is_array($errorResponse) || empty($errorResponse))   
    180.             {  
    181.                 return;  
    182.             }  
    183.             if (!isset($errorResponse['command'], $errorResponse['statusCode'], $errorResponse['identifier']))   
    184.             {  
    185.                 return;  
    186.             }  
    187.             if ($errorResponse['command'] != self::ERROR_RESPONSE_COMMAND)   
    188.             {  
    189.                 return;  
    190.             }  
    191.             $errorResponse['timeline'] = time();  
    192.             $errorResponse['statusMessage'] = 'None (unknown)';  
    193.             if (isset($this->_aErrorResponseMessages[$errorResponse['statusCode']]))   
    194.             {  
    195.                 $errorResponse['statusMessage'] = $this->_errorResponseMessages[$errorResponse['statusCode']];  
    196.             }  
    197.             return $errorResponse;  
    198.         }  
    199.   
    200.         protected function parseErrorMessage($errorMessage)  
    201.         {  
    202.             return unpack('Ccommand/CstatusCode/Nidentifier', $errorMessage);  
    203.         }  
    204.   
    205.         public function send()  
    206.         {  
    207.             if (!$this->_hSocket)   
    208.             {  
    209.                 throw new Exception  
    210.                 (  
    211.                     'Not connected to Push Notification Service'  
    212.                 );  
    213.             }  
    214.             $sendCount = $this->getDTNumber();  
    215.             $messagePayload = $this->getPayload();  
    216.             foreach($this->_deviceTokens AS $key => $value)  
    217.             {  
    218.                 $apnsMessage = $this->getBinaryNotification($value, $messagePayload, $messageID = 0, $Expire = 604800);  
    219.                 $nLen = strlen($apnsMessage);  
    220.                 $aErrorMessage = null;  
    221.                 if ($nLen !== ($nWritten = (int)@fwrite($this->_hSocket, $apnsMessage)))   
    222.                 {  
    223.                     $aErrorMessage = array  
    224.                     (  
    225.                         'identifier' => $key,  
    226.                         'statusCode' => self::STATUS_CODE_INTERNAL_ERROR,  
    227.                         'statusMessage' => sprintf('%s (%d bytes written instead of %d bytes)',$this->_errorResponseMessages[self::STATUS_CODE_INTERNAL_ERROR], $nWritten, $nLen)  
    228.                     );  
    229.                 }  
    230.             }  
    231.         }  
    232.   
    233.   
    234.         public function addDT($deviceToken)  
    235.         {  
    236.             if (!preg_match('~^[a-f0-9]{64}$~i', $deviceToken))   
    237.             {  
    238.                 throw new Exception  
    239.                 (  
    240.                     "Invalid device token '{$deviceToken}'"  
    241.                 );  
    242.             }  
    243.             $this->_deviceTokens[] = $deviceToken;  
    244.         }         
    245.           
    246.         public function getDTNumber()  
    247.         {  
    248.             return count($this->_deviceTokens);  
    249.         }  
    250.   
    251.         public function setText($text)  
    252.         {  
    253.             $this->_text = $text;  
    254.         }  
    255.   
    256.         public function getText()  
    257.         {  
    258.             return $this->_text;  
    259.         }  
    260.   
    261.         public function setBadge($badge)  
    262.         {  
    263.             if (!is_int($badge))   
    264.             {  
    265.                 throw new Exception  
    266.                 (  
    267.                     "Invalid badge number '{$badge}'"  
    268.                 );  
    269.             }  
    270.             $this->_badge = $badge;  
    271.         }  
    272.   
    273.         public function getBadge()  
    274.         {  
    275.             return $this->_badge;  
    276.         }  
    277.   
    278.         public function setSound($sound = 'default')  
    279.         {  
    280.             $this->_sound = $sound;  
    281.         }  
    282.   
    283.         public function getSound()  
    284.         {  
    285.             return $this->_sound;  
    286.         }  
    287.   
    288.         public function setCP($name, $value)  
    289.         {  
    290.             if ($name == self::APPLE_RESERVED_NAMESPACE)   
    291.             {  
    292.                 throw new Exception  
    293.                 (  
    294.                     "Property name '" . self::APPLE_RESERVED_NAMESPACE . "' can not be used for custom property."  
    295.                 );  
    296.             }  
    297.             $this->_customProperties[trim($name)] = $value;  
    298.         }  
    299.   
    300.         protected function _getPayload()  
    301.         {  
    302.             $aPayload[self::APPLE_RESERVED_NAMESPACE] = array();  
    303.   
    304.             if (isset($this->_text))   
    305.             {  
    306.                 $aPayload[self::APPLE_RESERVED_NAMESPACE]['alert'] = (string)$this->_text;  
    307.             }  
    308.             if (isset($this->_badge) && $this->_badge > 0)   
    309.             {  
    310.                 $aPayload[self::APPLE_RESERVED_NAMESPACE]['badge'] = (int)$this->_badge;  
    311.             }  
    312.             if (isset($this->_sound))   
    313.             {  
    314.                 $aPayload[self::APPLE_RESERVED_NAMESPACE]['sound'] = (string)$this->_sound;  
    315.             }  
    316.   
    317.             if (is_array($this->_customProperties))   
    318.             {  
    319.                 foreach($this->_customProperties as $propertyName => $propertyValue)   
    320.                 {  
    321.                     $aPayload[$propertyName] = $propertyValue;  
    322.                 }  
    323.             }  
    324.             return $aPayload;  
    325.         }  
    326.   
    327.         public function setExpiry($expiryValue)  
    328.         {  
    329.             if (!is_int($expiryValue))   
    330.             {  
    331.                 throw new Exception  
    332.                 (  
    333.                     "Invalid seconds number '{$expiryValue}'"  
    334.                 );  
    335.             }  
    336.             $this->_expiryValue = $expiryValue;  
    337.         }  
    338.   
    339.         public function getExpiry()  
    340.         {  
    341.             return $this->_expiryValue;  
    342.         }  
    343.   
    344.         public function setCustomIdentifier($customIdentifier)  
    345.         {  
    346.             $this->_customIdentifier = $customIdentifier;  
    347.         }  
    348.   
    349.         public function getCustomIdentifier()  
    350.         {  
    351.             return $this->_customIdentifier;  
    352.         }         
    353.   
    354.         public function getPayload()  
    355.         {  
    356.             $sJSONPayload = str_replace  
    357.             (  
    358.                 '"' . self::APPLE_RESERVED_NAMESPACE . '":[]',  
    359.                 '"' . self::APPLE_RESERVED_NAMESPACE . '":{}',  
    360.                 json_encode($this->_getPayload())  
    361.             );  
    362.             $nJSONPayloadLen = strlen($sJSONPayload);  
    363.   
    364.             if ($nJSONPayloadLen > self::PAYLOAD_MAXIMUM_SIZE)  
    365.             {  
    366.                 if ($this->_autoAdjustLongPayload)   
    367.                 {  
    368.                     $maxTextLen = $textLen = strlen($this->_text) - ($nJSONPayloadLen - self::PAYLOAD_MAXIMUM_SIZE);  
    369.                     if ($nMaxTextLen > 0)  
    370.                     {  
    371.                         while (strlen($this->_text = mb_substr($this->_text, 0, --$textLen, 'UTF-8')) > $maxTextLen);  
    372.                         return $this->getPayload();  
    373.                     }else  
    374.                     {  
    375.                         throw new Exception  
    376.                         (  
    377.                             "JSON Payload is too long: {$nJSONPayloadLen} bytes. Maximum size is " .  
    378.                             self::PAYLOAD_MAXIMUM_SIZE . " bytes. The message text can not be auto-adjusted."  
    379.                         );  
    380.                     }  
    381.                 }else  
    382.                 {  
    383.                     throw new Exception  
    384.                     (  
    385.                         "JSON Payload is too long: {$nJSONPayloadLen} bytes. Maximum size is " .  
    386.                         self::PAYLOAD_MAXIMUM_SIZE . " bytes"  
    387.                     );  
    388.                 }  
    389.             }  
    390.             return $sJSONPayload;  
    391.         }     
    392.     }  
    393.   
    394. ?>  
    395. <?php  
    396. date_default_timezone_set('PRC');  
    397. echo "we are young,test apns.  -".date('Y-m-d h:i:s',time());  
    398.   
    399. $rootpath = 'entrust_root_certification_authority.pem';  //ROOT证书地址  
    400. $cp = 'ck.pem';  //provider证书地址  
    401. $apns = new APNS(1,$cp);  
    402. try  
    403. {  
    404.     //$apns->setRCA($rootpath);  //设置ROOT证书  
    405.     $apns->connect(); //连接  
    406.     $apns->addDT('acc5150a4df26507a84f19ba145ca3c1be5842a6177511ce7c43d01badb1bd96');  //加入deviceToken  
    407.     $apns->setText('这是一条测试信息');  //发送内容  
    408.     $apns->setBadge(1);  //设置图标数  
    409.     $apns->setSound();  //设置声音  
    410.     $apns->setExpiry(3600);  //过期时间  
    411.     $apns->setCP('custom operation',array('type' => '1','url' => 'http://www.google.com.hk'));  //自定义操作  
    412.     $apns->send();  //发送  
    413.     echo ' sent ok';  
    414. }catch(Exception $e)  
    415. {  
    416.     echo $e;  
    417. }  
    418. ?>  
    419.   
    420. </body>  
    421. </html>  

    2,启动 Apache 

    mac 自带apache,可直接运行php。

    打开“终端(terminal)”,输入 sudo apachectl -v,可显示Apache的版本;

    输入 sudo apachectl start,这样Apache就启动了。

    编辑文件 /etc/apache2/httpd.conf  ,  把  LoadModule php5_module libexec/apache2/libphp5.so 前面的注释去掉;然后重启apache: sudo apachectl restart

    打开Safari浏览器地址栏输入 “http://localhost”,可以看到内容为“It works!”的页面。其位

    于“/Library/WebServer/Documents/”下,这就是Apache的默认根目录。

    3,如何调试

    将服务器端写好的apns.php 文件以及生成的 ck.pem 文件,直接拷贝到 /Library/WebServer/Documents/  下,在浏览器中,直接浏览: http://localhost/apns.php  。这样消息就发送到了苹果服务器。
    一天一章
  • 相关阅读:
    android29
    android28
    android27
    android26
    Dynamics CRM2011 MspInstallAction failed when installing an Update Rollup
    Dynamics CRM Import Solution Attribute Display Name description is null or empty
    The service cannot be activated because it does not support ASP.NET compatibility
    IIS部署WCF报 无法读取配置节“protocolMapping”,因为它缺少节声明
    Unable to access the IIS metabase.You do not have sufficient privilege
    LM算法与非线性最小二乘问题
  • 原文地址:https://www.cnblogs.com/hangman/p/5440997.html
Copyright © 2020-2023  润新知