Pergunta

I created a push notification server for my apps. This server works fine on test and production. I created an API which can recieve a serialized array of device tokens. This API is created in PHP, and the call to the API is PHP as well. This is the array:

  'devices' => serialize(array(
        //'aaaaaa',
        'bbbbb',
        'ccccc'
   )),

The real tokens are replaced by placeholders for security reasons, ofcourse. I got one token that screws up the push notifications for the other tokens. When I disable this token in the array (aaaaa in this case), the other 2 devices receive the token. When I enable the aaaa token, no push notification is delivered at all.

The API side to send the actual push:

 foreach($devices as $device) {
      $msg = chr(0) . pack('n', 32) . pack('H*', $device) . pack('n', strlen($payload)) . $payload;
      $result = fwrite($fp, $msg, strlen($msg));
 }

Ofcourse there is some variable explaination missing in the code above, but this works fine in the case without token aaa, so that shouldnt be the problem.

Anyone knows why one faulty token can screw up pushes for valid tokens?


EDIT: I now use the enhanced method for APNS, using this code:

$error = false;
foreach($devices as $deviceToken) {
    $msg = pack("C", 1) . pack("N", $deviceToken) . pack("N",  time() + 86400) . pack("n", 32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n", strlen($payload)) . $payload;
    fwrite($fp, $msg);
    $error = $this->checkAppleErrorResponse($fp);

    debug($error);
}

And the checkAppleErrorResponse function:

function checkAppleErrorResponse($fp) {

    $apple_error_response = fread($fp, 6);

    if ($apple_error_response) {

        $error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);

        if ($error_response['status_code'] == '0') {
            $error_response['status_code'] = '0-No errors encountered';
        } else if ($error_response['status_code'] == '1') {
            $error_response['status_code'] = '1-Processing error';
        } else if ($error_response['status_code'] == '2') {
            $error_response['status_code'] = '2-Missing device token';
        } else if ($error_response['status_code'] == '3') {
            $error_response['status_code'] = '3-Missing topic';
        } else if ($error_response['status_code'] == '4') {
            $error_response['status_code'] = '4-Missing payload';
        } else if ($error_response['status_code'] == '5') {
            $error_response['status_code'] = '5-Invalid token size';
        } else if ($error_response['status_code'] == '6') {
            $error_response['status_code'] = '6-Invalid topic size';
        } else if ($error_response['status_code'] == '7') {
            $error_response['status_code'] = '7-Invalid payload size';
        } else if ($error_response['status_code'] == '8') {
            $error_response['status_code'] = '8-Invalid token';
        } else if ($error_response['status_code'] == '255') {
            $error_response['status_code'] = '255-None (unknown)';
        } else {
            $error_response['status_code'] = $error_response['status_code'].'-Not listed';
        }

        echo 'Response Command:<b>' . $error_response['command'] . '</b>&nbsp;&nbsp;&nbsp;Identifier:<b>' . $error_response['identifier'] . '</b>&nbsp;&nbsp;&nbsp;Status:<b>' . $error_response['status_code'] . '</b><br>';

        echo 'Identifier is the rowID (index) in the database that caused the problem, and Apple will disconnect you from server. To continue sending Push Notifications, just start at the next rowID after this Identifier.<br>';

        return true;
    }

    return false;
}

Sending only the valid token, $numchanged is 0. Sending the invalid token and then the valid token, $numchanged is 1 for both cases.

Foi útil?

Solução

You are using the simple binary format for sending push notifications to Apple (starts with a 0 byte). With this format, Apple never return any responses, but when the APNS server encounters a bad notification (often caused by invalid device token, but there are other error types, such as payload too long), it closes the connection. Any messages that were sent after the faulty one won't be processed by Apple. In that case you have to open a new connection and resend the other messages.

If you switch to the more advanced formats (that start with a 1 byte or a 2 byte), Apple will send you an error response before closing the connection, and if you manage to read that response, you'll know the type of the error (for example 8 means invalid token) and the identifier of the faulty message. This lets you know which message was faulty, and therefore tells you which messages need to be resent (all the ones following the faulty message).

Read more about it here and here.

When you use the enhanced format, make sure to give a unique message ID to each notification you send. That's the ID you'll get back from Apple in an error response, so it must be unique for you to be able to identify the invalid notification.

Outras dicas

Following code alws works for me.

It will just ignore the faulty token as per my experience, it won't disturb other pushes beign sent to other valid tokens.

foreach ( $devices as $deviceToken ) {
    $apnsMessage = chr ( 0 ) . chr ( 0 ) . chr ( 32 ) . pack ( 'H*', str_replace ( ' ', '', $deviceToken ) ) . chr ( 0 ) . chr ( strlen ( $payload ) ) . $payload;
    if (fwrite ( $apns, $apnsMessage ) != FALSE) {
        $cnt_apple ++;
    }
}
echo "sent to $cnt_apple devices";
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top