简体   繁体   中英

Why most push notifications fail to reach the device

Not sure if it's the same issue as iPhone SDK Push notification randomly fails . But some notifications do arrive, I do have does "unpack" warnings when upacking command 8 packets.

I have a php file that pulls any notifications from a table (DB) which has token, payload, and device (iphone,ipad). This php runs every 15 seconds on the server cron. I don't know why 20% of the notifications arrive sometimes.... sometimes 80% arrive.... and sometime none arrive (I have 10 devices for testing). I read that apples notifications are "best effort" and that there are not guaranteed to arrive but this is ridiculous, something must be wrong with the logic of my code. Here is my code, I have changed names of directories, DB info and files for privacy reasons. Please help.

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


?>

I do have does "unpack" warnings when upacking command 8 packets

This implies that you are getting error responses from Apple, but from what I see in the code, you don't do anything with these errors beside logging them. Each such error response means that you sent invalid data to Apple, and as a result Apple closed the connection. This means any message sent after the invalid one will be discarded until you open a new connection.

The proper way to handle such responses is to parse the bad message ID contained in them, open a new connection to APNS, and re-send all the messages that you sent after the bad message.

The most likely error is invalid device token. That usually happen when you send a development token to production push environment or vice versa. If you clear the invalid device tokens from your DB, you are likely to see 100% of the messages arriving.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM