1

我正在尝试将 Apple 推送通知发送到我的应用程序。我正在关注 APNS 流程的本教程。网址:http ://www.raywenderlich.com/3525/apple-push-notification-services-tutorial-part-2 。

我在 MAMP、PHP 中几乎完成了工作,并且我在 pus_config.php 文件中提到了我的 .pem 文件以进行 APNS 连接。我已经用 simplepush.php 文件测试了我的 .pem 文件,并向我的设备发送了通知。现在正在尝试使用从上述教程中获取的 push.php 文件。

这是我的 push_config.php 文件,

<?php

// Configuration file for push.php

$config = array(
    // These are the settings for development mode
    'development' => array(

        // The APNS server that we will use
        'server' => 'gateway.sandbox.push.apple.com:2195',

        // The SSL certificate that allows us to connect to the APNS servers
        'certificate' => 'GopiAPNSCert.pem',
        'passphrase' => 'gopi',

        // Configuration of the MySQL database 
        'db' => array(
            'host'     => 'localhost',
            'dbname'   => 'gopipush',
            'username' => 'gopipush',
            'password' => 'uH4q9xQGNAuFW',
            ), 

        // Name and path of our log file
        'logfile' => '/Users/gopi/Desktop/PushChatServer/log/push_development.log',
        ),

    // These are the settings for production mode
    'production' => array(

        // The APNS server that we will use
        'server' => 'gateway.push.apple.com:2195',

        // The SSL certificate that allows us to connect to the APNS servers
        'certificate' => 'ck.pem',
        'passphrase' => 'secret',

        // Configuration of the MySQL database
        'db' => array(
            'host'     => 'localhost',
            'dbname'   => 'pushchat',
            'username' => 'pushchat',
            'password' => '1233}jfdoie',
            ),

        // Name and path of our log file
        'logfile' => '/Users/gopi/Desktop/PushChatServer/log/push_development.log',
        ),
    );

上述证书名称和密码已通过 simplepush.php 文件进行验证和正确测试。

这是我的 push.php 文件,

<?php

try
{
    require_once('push_config.php');

    ini_set('display_errors', 'off');

    if ($argc != 2 || ($argv[1] != 'development' && $argv[1] != 'production'))
        exit("Usage: php push.php development|production". PHP_EOL);

    $mode = $argv[1];
    $config = $config[$mode];

    writeToLog("Push script started ($mode mode)");

    $obj = new APNS_Push($config);
    $obj->start();
}
catch (Exception $e)
{
    fatalError($e);
}

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

function writeToLog($message)
{
    global $config;
    if ($fp = fopen($config['logfile'], 'at'))
    {
        fwrite($fp, date('c') . ' ' . $message . PHP_EOL);
        fclose($fp);
    }
}

function fatalError($message)
{
    writeToLog('Exiting with fatal error: ' . $message);
    exit;
}

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

class APNS_Push
{
    private $fp = NULL;
    private $server;
    private $certificate;
    private $passphrase;

    function __construct($config)
    {
        $this->server = $config['server'];
        $this->certificate = $config['certificate'];
        $this->passphrase = $config['passphrase'];

        // Create a connection to the database.
        $this->pdo = new PDO(
            'mysql:host=' . $config['db']['host'] . ';dbname=' . $config['db']['dbname'], 
            $config['db']['username'], 
            $config['db']['password'],
            array());


        // If there is an error executing database queries, we want PDO to
        // throw an exception.
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        // We want the database to handle all strings as UTF-8.
        $this->pdo->query('SET NAMES utf8');
    }

    // This is the main loop for this script. It polls the database for new
    // messages, sends them to APNS, sleeps for a few seconds, and repeats this
    // forever (or until a fatal error occurs and the script exits).
    function start()
    {
        writeToLog('Connecting to ' . $this->server);

        if (!$this->connectToAPNS())
        {
           exit;
        }


        while (true)
        {           
            writeToLog('Getting PushQueue');

            $stmt = $this->pdo->prepare('SELECT * FROM push_queue WHERE time_sent IS NULL LIMIT 20');
            $stmt->execute();
            $messages = $stmt->fetchAll(PDO::FETCH_OBJ);

            foreach ($messages as $message)
            {
                if ($this->sendNotification($message->message_id, $message->device_token, $message->payload))
                {
                    $stmt = $this->pdo->prepare('UPDATE push_queue SET time_sent = NOW() WHERE message_id = ?');
                    $stmt->execute(array($message->message_id));
                }
                else  // failed to deliver
                {
                    $this->reconnectToAPNS();
                }
            }

            unset($messages);           
            sleep(5);
        }
    }

    // Opens an SSL/TLS connection to Apple's Push Notification Service (APNS).
    // Returns TRUE on success, FALSE on failure.
    function connectToAPNS()
    {
        $ctx = stream_context_create();
        stream_context_set_option($ctx, 'ssl', 'local_cert', $this->certificate);
        stream_context_set_option($ctx, 'ssl', 'passphrase', $this->passphrase);


        $this->fp = stream_socket_client(
            'ssl://' . $this->server, $err, $errstr, 60,
            STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

        if (!$this->fp)
        {
            writeToLog('Failed APNS Connection');
            writeToLog("Failed to connect: $err $errstr");
            return FALSE;
        }

        writeToLog('Connection OK');
        return TRUE;
    }

    // Drops the connection to the APNS server.
    function disconnectFromAPNS()
    {
        fclose($this->fp);
        $this->fp = NULL;
    }

    // Attempts to reconnect to Apple's Push Notification Service. Exits with
    // an error if the connection cannot be re-established after 3 attempts.
    function reconnectToAPNS()
    {
        writeToLog('ReconnectToAPNS');
        $this->disconnectFromAPNS();

        $attempt = 1;

        while (true)
        {
            writeToLog('Reconnecting to ' . $this->server . ", attempt $attempt");

            if ($this->connectToAPNS())
                return;

            if ($attempt++ > 3)
                fatalError('Could not reconnect after 3 attempts');

            sleep(60);
        }
    }

    // Sends a notification to the APNS server. Returns FALSE if the connection
    // appears to be broken, TRUE otherwise.
    function sendNotification($messageId, $deviceToken, $payload)
    {
        if (strlen($deviceToken) != 64)
        {
            writeToLog("Message $messageId has invalid device token");
            return TRUE;
        }

        if (strlen($payload) < 10)
        {
            writeToLog("Message $messageId has invalid payload");
            return TRUE;
        }

        writeToLog("Sending message $messageId to '$deviceToken', payload: '$payload'");

        if (!$this->fp)
        {
            writeToLog('No connection to APNS');
            return FALSE;
        }

        // The simple format
        $msg = chr(0)                       // command (1 byte)
             . pack('n', 32)                // token length (2 bytes)
             . pack('H*', $deviceToken)     // device token (32 bytes)
             . pack('n', strlen($payload))  // payload length (2 bytes)
             . $payload;                    // the JSON payload

        /*
        // The enhanced notification format
        $msg = chr(1)                       // command (1 byte)
             . pack('N', $messageId)        // identifier (4 bytes)
             . pack('N', time() + 86400)    // expire after 1 day (4 bytes)
             . pack('n', 32)                // token length (2 bytes)
             . pack('H*', $deviceToken)     // device token (32 bytes) 
             . pack('n', strlen($payload))  // payload length (2 bytes)
             . $payload;                    // the JSON payload
        */

        $result = @fwrite($this->fp, $msg, strlen($msg));

        if (!$result)
        {
            writeToLog('Message not delivered');
            return FALSE;
        }

        writeToLog('Message successfully delivered');
        return TRUE;
    }
}

我的 push_development.log 文件中出现此错误。

2012-05-14T18:04:28+05:30 Push script started (development mode)
2012-05-14T18:04:28+05:30 Connecting to gateway.sandbox.push.apple.com:2195
2012-05-14T18:04:29+05:30 Failed to connect: 0 

我找不到我做错了什么?我应该在我的 push.php 文件中更改什么。我已经测试了我的网络连接和 .pem 文件。你能帮我解决这个错误并在我的设备中获取通知吗?提前致谢。

编辑

unknownc42c032e8297:~ name$ cd /Users/name/Desktop/PushChatServer
unknownc42c032e8297: PushChatServer name$ telnet gateway.sandbox.push.apple.com 2195
Trying 17.149.34.66...
Connected to gateway.sandbox.push-apple.com.akadns.net.
Escape character is '^]'.
^C
Connection closed by foreign host.
unknownc42c032e8297: PushChatServer name$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert NameAPNCert.pem -key GopiAPNSCert
.pem
Enter pass phrase for NameAPNKey.pem:
CONNECTED(00000003)
depth=1 /C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/C=US/ST=California/L=Cupertino/O=Apple Inc/OU=Internet Services/CN=gateway.sandbox.push.apple.com
   i:/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
 1 s:/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
   i:/O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Certification Authority (2048)
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEZTCCA02gAwIBAgIESyDhfjANBgkqhkiG9w0BAQUFADCBsTELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
Lm5ldC9ycGEgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
KGMpIDIwMDkgRW50cnVzdCwgSW5jLjEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eSAtIEwxQzAeFw0xMDA0MTMyMzM0MzNaFw0xMjA1MzEw
MDA0MjdaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAG
A1UEBxMJQ3VwZXJ0aW5vMRIwEAYDVQQKEwlBcHBsZSBJbmMxGjAYBgNVBAsTEUlu
dGVybmV0IFNlcnZpY2VzMScwJQYDVQQDEx5nYXRld2F5LnNhbmRib3gucHVzaC5h
cHBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM5NngiDMFpGBMmb
8tG2MRhLEdsx553Xjq+5C/c0mtildwhnC1X0LWKUexWdQsMchniac+WnHFSs3YMJ
JJ55kQSB6wqK/WNcxsUn8pMkMsvk3YZFM7TsaKQvFOeieiXCSJVlR3grm3+dilv1
Br+SUqv8JrgU3ijmoQO63vkb8B/hAgMBAAGjggEnMIIBIzALBgNVHQ8EBAMCBaAw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMDMGA1UdHwQsMCowKKAmoCSG
Imh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvbGV2ZWwxYy5jcmwwMwYIKwYBBQUHAQEE
JzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDBABgNVHSAE
OTA3MDUGCSqGSIb2fQdLAjAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1
c3QubmV0L3JwYTAfBgNVHSMEGDAWgBQe8auJBvhJDwEzd+4Ueu4ZfJMoTTAdBgNV
HQ4EFgQUNyg/64Sjw/+b4YOwC8E/c+jemRgwCQYDVR0TBAIwADANBgkqhkiG9w0B
AQUFAAOCAQEAk9Ij+NCp+323+4vBqbA0fT9ZCROptPqNIshY5uEvaWUaW5hsoLUm
fsMJMueqzDaoj4yPD8iCCZq1Mp8tM8WB2mG1zIxTLshlhRgDDUF11IbUUBHv/ZhU
RzXewQD6pazQmsBPuR0vP3mmWbKqmZOiv2yWSGlQmWGW4m6RQwjYYj8UqqFEdinV
g1+qY6/muTpaCiygDjJZBlv9P6bwwP9FB8OJf5tGECvvxXad3PK/oiI77aLTYSVr
SA0oisXCiqcgTKQq5BV5M3fQQ4ZS73aBKLI0wPYc0AASD5WdtPTGTvmEbhO4KeaU
0SL85Prf8uSsDOLvn3656awLz/H/yzrf/g==
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Cupertino/O=Apple Inc/OU=Internet Services/CN=gateway.sandbox.push.apple.com
issuer=/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
---
No client certificate CA names sent
---
SSL handshake has read 2549 bytes and written 2017 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES256-SHA
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 729CC0899B36143DAC78D40B2C31ECB71C81A3BD8DC5CFD6D71AC7885DD2E63DCD47096E97A1B3AF032A8D7D48BF73DA
    Key-Arg   : None
    Start Time: 1336636910
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
name
closed

unknownc42c032e8297: PushChatServer name$ php simplepush.php
Connected to APNS
Message successfully delivered

在这里,我可以连接 apns 并发送通知。所以认为 .pem 文件没有问题。

4

2 回答 2

3

[编辑] 您可以尝试在配置中设置证书文件的绝对路径吗?

您可以检查此项以解决对等验证问题。

安装证书 http://code.google.com/p/apns-php/wiki/CertificateCreation#Verify_peer_using_Entrust_Root_Certification_Authority

在这里使用

stream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');

注意:禁用验证也有效。

于 2012-05-14T15:03:54.110 回答
0

对我有用的是更新 ck.pem 文件的权限,以便 PHP 脚本可以正确读取它。完成后,我可以毫无问题地连接到 APNS。

于 2013-05-21T11:53:14.737 回答