GeekerProbe

水滴石穿 Keeping faith.      --- wtlucky's Blog

APNS服务器搭建ssl错误问题解决方案

| Comments

最近再做一个推送项目,需要搭建APNS服务器,再将PHP代码部署到Server上时遇到了如下错误:

APNS ssl error
1
2
3
4
5
6
7
8
Warning: stream_socket_client() [function.stream-socket-client]: Unable to set local cert chain file `ck.pem'; Check that your cafile/capath settings include details of your certificate and its issuer in D:\AppServ\www\push1\push.php on line 24

Warning: stream_socket_client() [function.stream-socket-client]: failed to create an SSL handle in D:\AppServ\www\push1\push.php on line 24

Warning: stream_socket_client() [function.stream-socket-client]: Failed to enable crypto in D:\AppServ\www\push1\push.php on line 24

Warning: stream_socket_client() [function.stream-socket-client]: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in D:\AppServ\www\push1\push.php on line 24
Failed to connect: 0

上网Google之,发现很多人遇到此问题,给出的解决方案照做后错误依然存在。 最终还是自己解决,从问题本身出发吧,自己当初调试时Server是部署在Mac os上的,而现在却要部署在Windows Server 2008上。所以很可能是两边的配置出了问题,而代码的第24行为:

1
2
3
4
5
<?php
$fp = stream_socket_client(
        'ssl://gateway.sandbox.push.apple.com:2195', $err,
        $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
?>

PHP给出的错误大概是说,没有找到证书,无法建立ssl连接。

首先.pem证书已经制作,并且可用。还有就是证书的路径需要放正确

1
2
3
<?php
stream_context_set_option($ctx, 'ssl', 'local_cert',$this->localcert);
?>

确保该函数的最后一个参数所指的路径能正确找到证书。 经过测试在Mac OS下这样就可以了,但是在Windows下要改成这样:

1
2
3
<?php
stream_context_set_option($ctx, 'ssl', 'local_cert',dirname(__FILE__) . '\\' .$this->localcert);
?>

但是做了这些,问题仍然不能解决,剩下的问题就是Apache需要开启ssl模块,通过查看Apache官方文档得知,使用ssl需要Apache开启三个支持模块分别是:

  1. mod_include
  2. mod_cgi
  3. mod_expires

接下来打开Apache的配置文件httpd.conf大概50-100行之间模块加载部分,放开这三个模块加载前的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LoadModule reqtimeout_module libexec/apache2/mod_reqtimeout.so
LoadModule ext_filter_module libexec/apache2/mod_ext_filter.so
LoadModule include_module libexec/apache2/mod_include.so          #注释放开
LoadModule filter_module libexec/apache2/mod_filter.so
LoadModule substitute_module libexec/apache2/mod_substitute.so
LoadModule deflate_module libexec/apache2/mod_deflate.so
LoadModule log_config_module libexec/apache2/mod_log_config.so
LoadModule log_forensic_module libexec/apache2/mod_log_forensic.so
LoadModule logio_module libexec/apache2/mod_logio.so
LoadModule env_module libexec/apache2/mod_env.so
LoadModule mime_magic_module libexec/apache2/mod_mime_magic.so
LoadModule cern_meta_module libexec/apache2/mod_cern_meta.so
LoadModule expires_module libexec/apache2/mod_expires.so         #注释放开
LoadModule headers_module libexec/apache2/mod_headers.so
LoadModule ident_module libexec/apache2/mod_ident.so

保存,重启Apache,再试,问题已解决。

最后附上推送部分的PHP代码:

(push.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php

class Push {
    public $deviceToken;//需要在构造时候设置

    //本地证书和密码
    public $localcert ='';
    public $passphrase = '';

    /*
     * 功能:构造函数,设置deviceToken
    */
    function Push($deviceToken)
    {
        $this->deviceToken = $deviceToken;
    }
    /*
    功能:生成发送内容并且转化为json格式
    */

    private function createPayload($message,$type,$sound)
    {
        // Create the payload body
        $body['aps'] = array(
            'alert' => $message,
            'sound' => $sound,
            'type' =>$type
        );

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

        return $payload;
    }

    // Put your private key's passphrase here:
   public function  pushData($message,$type,$sound)
    {

        $ctx = stream_context_create();
        stream_context_set_option($ctx, 'ssl', 'local_cert',$this->localcert);
        stream_context_set_option($ctx, 'ssl', 'passphrase', $this->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;


        // 创建消息
        $payload =$this->createPayload($message,$type,$sound);

        // Build the binary notification
        $msg = chr(0) . pack('n', 32) . pack('H*', $this ->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);
    }
   }
?>

Comments