为啥大多数推送通知无法到达设备

Posted

技术标签:

【中文标题】为啥大多数推送通知无法到达设备【英文标题】:Why most push notifications fail to reach the device为什么大多数推送通知无法到达设备 【发布时间】:2014-05-15 16:39:15 【问题描述】:

不确定它是否与 iPhone SDK Push notification randomly fails 相同。但是有些通知确实到达了,我在打包命令 8 数据包时确实有“解包”警告。

我有一个 php 文件,它从具有令牌、有效负载和设备(iphone、ipad)的表 (DB) 中提取任何通知。这个 php 在服务器 cron 上每 15 秒运行一次。我不知道为什么 20% 的通知有时会到达……有时 80% 会到达……有时没有通知(我有 10 台设备用于测试)。我读到苹果通知是“尽力而为”,并且不能保证会到达,但这很荒谬,我的代码逻辑一定有问题。这是我的代码,出于隐私原因,我更改了目录、数据库信息和文件的名称。请帮忙。

<?php
    include("/#####/DB.conf");
    $link = mysql_connect($host,$userDB,$passDB) or die("Failed to connect: " . mysql_error());
    mysql_select_db('#######') or die("Failed to connect: " . mysql_error());
    mysql_query("SET NAMES 'utf8'");
    $certFile_iphone = '/##########/push_production.pem';
    $certFile_ipad   = '/########/push_production.pem';
    $passphrase = '#########';

    $sent += sendPush($certFile_iphone,$passphrase,'iphone');
    $borrados += checkFeedback($certFile_iphone,$passphrase,'iphone');
    $sent += sendPush($certFile_ipad,$passphrase,'ipad');
    $borrados += checkFeedback($certFile_ipad,$passphrase,'ipad');

    //////////////////////////////
    //Send notification to users//
    //////////////////////////////
    function sendPush($cert,$pass,$device)
    
        $ctx = stream_context_create();
        stream_context_set_option($ctx, 'ssl', 'local_cert', $cert);
        stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);

        // Open a connection to the APNS server
        $fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 600, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

        if (!$fp)
            exit("Failed to connect: $err $errstr" . PHP_EOL);
        //gets all the devices of that type and their current notification to send
        $query = mysql_query("SELECT * FROM pushNotification_buffer WHERE device = '".$device."' LIMIT 1000;");

        $errors = 0;
        $sent = 0;
        $i = 0;
        while($row = mysql_fetch_array($query))
        

            $payload = $row['payload'];
            $deviceToken = $row['identificador'];
            //$msg = chr(0) . pack('n', 32) . pack('H*', $row['identificador']) . pack('n', strlen($payload)) . $payload;
            $msgInner =
            chr(1)
            . pack('n', 32)
            . pack('H*', $deviceToken)

            . chr(2)
            . pack('n', strlen($payload))
            . $payload

            . chr(3)
            . pack('n', 4)
            . chr($i)

            . chr(4)
            . pack('n', 4)
            . pack('N', time() + 86400)

            . chr(5)
            . pack('n', 1)
            . chr(10)
            ;

            $msg=
            chr(2)
            . pack('N', strlen($msgInner))
            . $msgInner
            ;

            $i++;
            $result = fwrite($fp, $msg, strlen($msg));
            if (!$result)
            
                $errors++;
                usleep(1000000);
                $result = fwrite($fp, $msg, strlen($msg));
                error_log("Error on Device (2): " . $row['identificador']);
            

            if (!$result)
            
                $errors++;
                fclose($fp);
                return $sent;
            
            else
            
                $sent++;
                mysql_query("DELETE FROM pushNotification_buffer WHERE identificador = '".$row['identificador']."' AND payload = '".mysql_real_escape_string($row['payload'])."' LIMIT 1;");
            
            error_response($fp);
        

        fclose($fp);

        return $sent;

    
    /////////////////
    //APNS FeedBack//
    /////////////////
    function checkFeedback($cert,$pass,$device)
        $ctx = stream_context_create();
        stream_context_set_option($ctx, 'ssl', 'local_cert', $cert);
        stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
        stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);

        $fp = stream_socket_client('ssl://feedback.push.apple.com:2196', $error, $errorString, 600, STREAM_CLIENT_CONNECT, $ctx);

        $toDelete = 0;

        if(!$fp) echo ("NOTICE: Failed to connect to device: $error - $errorString.");

        while ($devcon = fread($fp, 38))
            $arr = unpack("H*", $devcon);
            $rawhex = trim(implode("", $arr));
            $token = substr($rawhex, 12, 64);
            if(!empty($token))
            
                if($device == 'iphone')
                    mysql_query("DELETE FROM iphoneDevicesAPNS WHERE identificador = '" . $token . "' LIMIT 1;");
                else
                    mysql_query("DELETE FROM ipadDevicesAPNS WHERE identificador = '" . $token . "' LIMIT 1;");
                
                $toDelete++;
            
        
        fclose($fp);
        return $toDelete;
    

    /////////////////////////////
    //Error response, command 8//
    /////////////////////////////
    function error_response($fp)
        $read = array($fp);
        $null = null;
        $changedStreams = stream_select($read, $null, $null, 0, 1000000);

        if ($changedStreams === false)
        
            echo ("Error: Unabled to wait for a stream availability");
        
        elseif ($changedStreams > 0)
            $responseBinary = fread($fp, 6);
            if ($responseBinary !== false || strlen($responseBinary) == 6)
                $response = unpack('Ccommand/Cstatus_code/Nidentifier', $responseBinary);
                var_dump($response);
                if (strlen($response["command"] > 0) || strlen($response["status_code"] > 0) || strlen($response["identifier"] > 0))
                    $texto=
                    "------------------------------------------". "\n".date("Y-m-d G:i:s"). "\n";
                    "id: " . $response["identifier"] . "\n" .
                    "code: ". $response["status_code"] . "\n" .
                    "command: ". $response["command"] . "\n". "\n"
                    ;
                    file_put_contents ( "/#####/logAPNS.log" ,$texto, FILE_APPEND);
                
            
        
    


?>

【问题讨论】:

【参考方案1】:

我在打包命令 8 数据包时确实有“解包”警告

这意味着您收到了来自 Apple 的错误响应,但从我在代码中看到的情况来看,除了记录这些错误之外,您不会对这些错误进行任何处理。每个此类错误响应都意味着您向 Apple 发送了无效数据,因此 Apple 关闭了连接。这意味着在无效消息之后发送的任何消息都将被丢弃,直到您打开新连接。

处理此类响应的正确方法是解析其中包含的错误消息 ID,打开与 APNS 的新连接,然后重新发送您在错误消息之后发送的所有消息。

最可能的错误是设备令牌无效。当您将开发令牌发送到生产推送环境时,通常会发生这种情况,反之亦然。如果您从数据库中清除无效的设备令牌,您可能会看到 100% 的消息到达。

【讨论】:

我只记录的原因是因为我没有看到任何错误......或者至少我没有正确记录它......我今天的日志看起来像这样: ----- ------------------------------------- 2014-05-15 10:59:05 --- --------------------------------------- 2014-05-15 10:59:07 - ----------------------------------------- 2014-05-15 11:02: 04 ------------------------------------------ 2014-05-15 11: 02:08 ------------------------------------------ 2014-05-15 11:02:17 我将从数据库中删除每个令牌并重新注册每个令牌以查看会发生什么 @user2387149 那你说的I do have does "unpack" warnings when upacking command 8 packets是什么意思?命令 8 数据包是错误响应。

以上是关于为啥大多数推送通知无法到达设备的主要内容,如果未能解决你的问题,请参考以下文章

如果禁用 SNS 端点,为啥 SNS 无法发送推送通知?

为啥推送通知ios在重启设备后会起作用?

为啥 GCM 不在 android 设备中提供推送通知?

节点 js 中的 EWS 推送通知

Android 设备接收无休止的推送通知

为啥推送通知不能在 iOS 中使用 Firebase 从设备到设备?