2013-08-22 19 views
9

EDIT - Utilizzando il formato binario miglioratooperazione APNS SSL non riuscita con codice 1

Risulta non stavo usando il formato binario migliorata così ho cambiato il mio codice.

<?php 

$message = $_POST['message']; 
$passphrase = $_POST['pass']; 

//Connect to db 


if ($db_found) { 

// Create the payload body 
$body['aps'] = array(
    'alert' => $message, 
    'sound' => 'default' 
); 

$streamContext = stream_context_create(); 
stream_context_set_option($streamContext, 'ssl', 'local_cert', 'x.pem'); 
stream_context_set_option($streamContext, 'ssl', 'passphrase', $passphrase); 

$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $error, $errorString, 15, STREAM_CLIENT_CONNECT, $streamContext); 
stream_set_blocking ($fp, 0); 

if (!$fp) 
    exit("Failed to connect: $err $errstr" . PHP_EOL); 

echo 'Connected to APNS for Push Notification' . PHP_EOL; 

// Keep push alive (waiting for delivery) for 90 days 
$apple_expiry = time() + (90 * 24 * 60 * 60); 



$tokenResult = //SQL QUERY TO GET TOKENS 

while($row = mysql_fetch_array($tokenResult)) { 
    $apple_identifier = $row["id"]; 
    $deviceToken = $row['device_id']; 
    $payload = json_encode($body); 

    // Enhanced Notification 
    $msg = pack("C", 1) . pack("N", $apple_identifier) . pack("N", $apple_expiry) . pack("n", 32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n", strlen($payload)) . $payload; 

    // SEND PUSH 
    fwrite($fp, $msg); 

    // We can check if an error has been returned while we are sending, but we also need to 
    // check once more after we are done sending in case there was a delay with error response. 
    checkAppleErrorResponse($fp); 
} 

// Workaround to check if there were any errors during the last seconds of sending. 
// Pause for half a second. 
// Note I tested this with up to a 5 minute pause, and the error message was still available to be retrieved 
usleep(500000); 

checkAppleErrorResponse($fp); 

echo 'Completed'; 

fclose($fp); 


// SIMPLE BINARY FORMAT 
/*for($i = 0; $i<count($deviceToken); $i++) { 

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

    // Build the binary notification 
    $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken[$i]) . pack('n', strlen($payload)) . $payload; 

    // Send it to the server 
    $result = fwrite($fp, $msg, strlen($msg)); 

    $bodyError .= 'result: '.$result.', devicetoken: '.$deviceToken[$i].''; 

    if (!$result) { 
     $errCounter = $errCounter + 1; 
     echo 'Message not delivered' . PHP_EOL; 
    } 
    else 
     echo 'Message successfully delivered' . PHP_EOL; 
}*/ 


// Close the connection to the server 
//fclose($fp); 


//Insert message into database 

mysql_close($db_handle); 

} 

else { 

    print "Database niet gevonden "; 
    mysql_close($db_handle); 
} 

// FUNCTION to check if there is an error response from Apple 
// Returns TRUE if there was and FALSE if there was not 
function checkAppleErrorResponse($fp) { 

//byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID). 
// Should return nothing if OK. 

//NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait 
// forever when there is no response to be sent. 

$apple_error_response = fread($fp, 6); 

if ($apple_error_response) { 

    // unpack the error response (first byte 'command" should always be 8) 
    $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 '<br><b>+ + + + + + ERROR</b> 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; 
} 

?> 

Durante l'utilizzo di questo nuovo codice non riesco ancora a inviare più di 300 + messaggi a causa di questo errore:

Warning: fwrite() [function.fwrite]: SSL operation failed with code 1. OpenSSL Error messages: error:1409F07F:SSL routines:SSL3_WRITE_PENDING:bad write retry in PATH_TO_SCRIPT.php on line NUMBER 

questo codice funziona bene quando si invia a pochi messaggi push.

DOMANDA VECCHIO con formato binario semplice Così ho integrato Notifiche Push molto tempo fa e stava funzionando benissimo per i messaggi inviati a meno di 500 persone. Ora sto cercando di inviare una notifica push a più di 1000 persone, ma poi ho l'errore rotto

Warning: fwrite() [function.fwrite]: SSL: Broken pipe in PATH_TO.PHP on line x 

Ho letto la documentazione di mele e so che i token non validi possono causare rete per disinserire. Alcune soluzioni raccomandano online su disconnessioni rilevamento e ricollegare come questo:

Your server needs to detect disconnections and reconnect if necessary. Nothing is 
"instant" when networking is involved; there's always some latency and code needs to take 
that into account. Also, consider using the enhanced binary interface so you can check the 
return response and know why the connection was dropped. The connection can also be 
dropped as a result of TCP keep-alive, which is outside of Apple's control. 

Sono in corso anche una valutazione di servizio che rileva i token non validi (utenti che volevano Notifiche Push, ma eliminato l'applicazione) e che solo funziona bene. Quello script php fa eco agli ID cancellati e posso confermare che quei token sono stati cancellati dal nostro database MySQL.

Come è possibile rilevare una disconnessione o una conduttura rotta e reagire in modo che le mie notifiche push possano raggiungere più di 1000 persone?

Attualmente sto usando questo semplice script push.php.

<?php 

$message = $_POST['message']; 
$passphrase = $_POST['pass']; 

//Connect to database stuff 

if ($db_found) { 
     $streamContext = stream_context_create(); 
     stream_context_set_option($streamContext, 'ssl', 'local_cert', 'x.pem'); 
     stream_context_set_option($streamContext, 'ssl', 'passphrase', $passphrase); 

     $fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $error, $errorString, 15, STREAM_CLIENT_CONNECT, $streamContext); 

if (!$fp) 
    exit("Failed to connect: $err $errstr" . PHP_EOL); 

echo 'Connected to APNS for Push Notification' . PHP_EOL; 

$deviceToken[] = //GET ALL TOKENS FROM DATABASE AND STORE IN ARRAY 

for($i = 0; $i<count($deviceToken); $i++) { 
    // Create the payload body 
    $body['aps'] = array(
    'alert' => $message, 
    'sound' => 'default' 
    ); 

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

    // Build the binary notification 
    $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken[$i]) . pack('n', strlen($payload)) . $payload; 

    // Send it to the server 
    $result = fwrite($fp, $msg, strlen($msg)); 

    $bodyError .= 'result: '.$result.', devicetoken: '.$deviceToken[$i].''; 

    if (!$result) { 
     $errCounter = $errCounter + 1; 
     echo 'Message not delivered' . PHP_EOL; 
    } 
    else 
     echo 'Message successfully delivered' . PHP_EOL; 
} 


echo $bodyError; 

// Close the connection to the server 
fclose($fp); 


//CODE TO SAVE MESSAGE TO DATABSE HERE 

if (!mysql_query($SQL,$db_handle)) { 
    die('Error: ' . mysql_error()); 
} 

} 
else { 
    print "Database niet gevonden "; 
    mysql_close($db_handle); 
} 


?> 

Anche fwrite restituisce 0 byte scritti quando si verifica l'errore SLL Broken Pipe.

Devo anche dire che non sono uno sviluppatore PHP o web ma uno sviluppatore di app quindi le mie capacità di php non sono così buone.

+0

Si utilizza il semplice formato binario, che non restituisce risposte. È necessario utilizzare il formato avanzato per ottenere risposte di errore. Vedi [questa risposta] (http://stackoverflow.com/questions/17724614/using-push-notification-usingf-api/17726433#17726433) per maggiori dettagli. – Eran

+0

Grazie per quello. Sto usando il formato enchanced ora e restituisce questo errore quando invio una grande quantità di messaggi (non durante l'invio di pochi messaggi): Avviso: fwrite() [function.fwrite]: operazione SSL fallita con il codice 1. Messaggi di errore OpenSSL: errore: 1409F07F: routine SSL: SSL3_WRITE_PENDING: nuovo tentativo di scrittura in PATH_TO_PHP_FILE_ON_LINE –

+0

Guarda [questa domanda] (http://stackoverflow.com/questions/2997218/why-am-i-getting-error1409f07fssl-routinesssl3- write-pending-bad-write-retr), aiuta? – LombaX

risposta

3

Quando si esegue:

fwrite($fp, $msg); 

si sta tentando di scrivere alla presa. Se qualcosa va storto, fwrite restituirà false o 0 (a seconda della versione di php) come valore di ritorno. Quando succede, devi gestirlo. avete due possibilità:

  • scartare l'intera operazione
  • riprovare l'ultima operazione di scrittura

se si sceglie la seconda opzione, si deve fare una nuova fwrite($fp, $msg) con il $ fp STESSA e $ msg dell'operazione fallita fwrite(). Se si modificano i parametri, un errore di 1409F07F:SSL viene restituito

Inoltre, ci sono situazioni in cui il fwrite non riesce a scrivere solo "alcuni byte", si dovrebbe gestire anche questa situazione, confrontando il valore restituito con la lunghezza di $ msg . In questo caso, è necessario inviare la parte rimanente del messaggio, ma in alcune situazioni è necessario inviare nuovamente l'intero messaggio (in base allo this link).

Dai un'occhiata alla riferimento fwrite e commenti: Link

+0

Quindi in pratica ho bisogno di controllare la lunghezza di $ msg con strlen e l'output di fwrite e confrontarli. Se non sono uguali, allora scrivere di nuovo con gli stessi valori? Ma cosa succede se i token non sono validi o qualcosa del genere. Lo script non si blocca in questo ciclo infinito? –

+0

Qualcosa come questo, sì. Per evitare loop infiniti è necessario impostare un numero massimo di tentativi. Inoltre, ti suggerisco di aspettare un attimo (dormi?) Prima di riprovare: se l'errore è dovuto al fatto che un buffer è pieno, gli darai la possibilità di liberare spazio. Per quanto riguarda i tentativi, in teoria dovresti riprovare: con l'intero messaggio, se i byte sono 0, o con la parte restante del messaggio se i byte sono più di 0, ma inferiore alla lunghezza totale. Tuttavia, sembra che in alcuni casi (a seconda del lato server) devi inviare l'intero messaggio anche in quest'ultimo caso. Provalo – LombaX

+0

Quale sarebbe un buon numero di tentativi e ore di sonno? 5x e dormi 3s? –

2

Non si può dare il codice PHP vero e proprio, dal momento che non so PHP, ma qui è la logica si dovrebbe usare (secondo Apple):

Push Notification Throughput and Error Checking

If you're seeing throughput lower than 9,000 notifications per second, your server might benefit from improved error handling logic.

Here's how to check for errors when using the enhanced binary interface. Keep writing until a write fails. If the stream is ready for writing again, resend the notification and keep going. If the stream isn't ready for writing, see if the stream is available for reading.

If it is, read everything available from the stream. If you get zero bytes back, the connection was closed because of an error such as an invalid command byte or other parsing error. If you get six bytes back, that's an error response that you can check for the response code and the ID of the notification that caused the error. You'll need to send every notification following that one again.

Once everything has been sent, do one last check for an error response.

It can take a while for the dropped connection to make its way from APNs back to your server just because of normal latency. It's possible to send over 500 notifications before a write fails because of the connection being dropped. Around 1,700 notifications writes can fail just because the pipe is full, so just retry in that case once the stream is ready for writing again.

Now, here's where the tradeoffs get interesting. You can check for an error response after every write, and you'll catch the error right away. But this causes a huge increase in the time it takes to send a batch of notifications.

Device tokens should almost all be valid if you've captured them correctly and you're sending them to the correct environment. So it makes sense to optimize assuming failures will be rare. You'll get way better performance if you wait for write to fail or the batch to complete before checking for an error response, even counting the time to send the dropped notifications again.

None of this is really specific to APNs, it applies to most socket-level programming.

If your development tool of choice supports multiple threads or interprocess communication, you could have a thread or process waiting for an error response all the time and let the main sending thread or process know when it should give up and retry.

Questo è preso dalla nota tecnica di Apple: Troubleshooting Push Notifications.

EDIT

Io non so come si rileva in PHP che ha fallito la scrittura, ma quando lo fa, si dovrebbe tentare di scrivere la notifica non è riuscito ancora una volta, e se non riesce ancora una volta, cercare di leggere la risposta all'errore e chiudere la connessione.

Se si riesce a leggere la risposta all'errore, si saprà quale notifica non è riuscita e si conoscerà il tipo di errore (l'errore più probabile è 8 - token dispositivo non valido). Se dopo aver scritto 100 messaggi si ottiene una risposta di errore per l'ottantesimo messaggio, è necessario inviare nuovamente i messaggi da 81 a 100, poiché Apple non li ha mai ricevuti. Nel mio caso (server Java), non riesco sempre a leggere la risposta all'errore (a volte ricevo un errore quando provo a leggere la risposta dal socket). In tal caso, posso solo spostare su un invio le notifiche successive (e non ho modo di sapere quali notifiche sono state effettivamente ricevute da Apple). Ecco perché è importante mantenere pulito il tuo database di token non validi.

In ogni caso, non si dovrebbe rimanere bloccati in un ciclo infinito, poiché quando si riceve un errore dopo aver inviato N notifiche, non si invieranno di nuovo le notifiche N. A meno che tu non riesca a leggere una risposta di errore da Apple (nel qual caso sai esattamente cosa inviare), rispedisci l'ultima notifica e anche se quella notifica è quella con il token non valido, probabilmente ottenere l'errore successivo dopo l'invio di ulteriori notifiche (che è sfortunato, dal momento che sarebbe stato molto più facile rilevare i token non validi se si otterrebbero immediatamente i guasti).

Se mantieni pulito il tuo database (cioè memorizzi solo i token dispositivo che sono stati inviati alla tua app da Apple, e tutti appartengono allo stesso ambiente push - sandbox o produzione), non dovresti incontrare nessuno token dispositivo non validi.

I token dispositivo restituiti dal servizio di feedback non sono token non validi. Sono token validi dei dispositivi che hanno disinstallato la tua app. I token non validi non sono mai stati validi per l'ambiente push corrente e non lo saranno mai. L'unico modo per identificare i token non validi è leggere le risposte di errore di Apple.

EDIT2:

ho dimenticato di dire prima. Ho riscontrato un problema simile al tuo quando si implementava il lato server di notifica push in Java. Non sono riuscito a ottenere in modo affidabile tutte le risposte di errore restituite da Apple.

Ho scoperto che in Java esiste un modo per disabilitare l'algoritmo di TCP Nagle, che causa il buffering di più messaggi prima di inviarli in un batch ad Apple.Sebbene Apple ci incoraggi a utilizzare l'algoritmo di Nagle (per motivi di prestazioni), ho scoperto che quando lo disattivo e poi provo a leggere la risposta di Apple dopo ogni messaggio che invio a loro, riesco a ricevere il 100% delle risposte di errore (I verificato tramite la scrittura di un processo che simulava il server APNS).

Disattivando l'algoritmo di Nagle e inviando le notifiche una alla volta, lentamente e tentando di leggere la risposta all'errore dopo ogni messaggio, è possibile individuare tutti i token non validi nel DB e rimuoverli. Una volta che sai che il tuo DB è pulito, puoi abilitare l'algoritmo di Nagle e riprendere l'invio delle notifiche rapidamente senza preoccuparti di leggere le risposte agli errori di Apple. Quindi, ogni volta che si verifica un errore durante la scrittura di un messaggio sul socket, è sufficiente creare un nuovo socket e riprovare a inviare solo l'ultimo messaggio.

+0

Grazie per le informazioni. Il mio problema però è che so che cosa sta andando storto e tutta la teoria sull'argomento, ma non conosco il PHP. Ci sono così tanti esempi di apns con php, ma non riescono tutti a inviare una grande quantità di notifiche perché nessuno di loro controlla la presenza di errori. –

+0

Btw non è bloccato su php o altro, ma ho pensato che fosse facile da implementare. Se esiste un'altra alternativa semplice (e gratuita) in grado di risolvere il problema, anch'io sarei felice. –

0

usare Google ho trovato alcune cose di interesse

http://rt.openssl.org/Ticket/Display.html?id=598&user=guest&pass=guest

Come il commento di patch dice

first check if there is a SSL3_BUFFER still being written out. This will happen with non blocking IO

Risposta del Why am I getting "error:1409F07F:SSL routines:SSL3_WRITE_PENDING: bad write retry" error while attempting an SSL_write? dice:

SSL_Write returns with SSL_ERROR_WANT_WRITE or SSL_ERROR_WANT_READ, you have to repeat the call to SSL_write with the same parameters again, after the condition is satisfied.

Forse il buffer ssl sta ancora scrivendo quando provi a scrivere, puoi c diamine se il buffer non sta scrivendo, riprovare, o limitare il buffer potrebbe abbastanza.

Duplicati:

aggiuntive (edit)

Soprattutto cerco di dire che è necessario trovare un modo per determinare se presa non sta scrivendo quando tenti di scrivere di nuovo e poi scrivi.

Se si dispone di un modo per farlo, provare:

  • La disattivazione del blocco non-blocking
  • Rerty la scrittura

    while(!fwrite($fp, $msg)) { 
        usleep(400000); //400 msec 
    } 
    

    se è successo, basta disabilitare gli erori via error_reporting non usa mai l'operatore @.

  • Impostazione stream_set_write_buffer() a 0
+0

Questo secondo link non ha senso per me, ma sembra facile. Cos'è esattamente $ buffer? Ancora una volta fallisco @ php –

+0

Il concetto di buffer è troppo difficile da spiegare per me adesso e con il mio livello di inglese, forse wikipedia o qualcosa di simile può aiutarti meglio, guarda le modifiche, ne leggo un po '. –

1

La mia soluzione (per l'ormai semi-vecchia questione) è stato che ho avuto un po 'di sviluppo-ambiente APN gettoni nel mio database tenta di inviare ad una produzione-ambiente. Una volta che mi sono sbarazzato di loro dal mio database, il resto ha funzionato bene. Sfortunatamente, su oltre 7.000 APN, non ero sicuro di quali token fossero negativi, quindi ho dovuto cancellarli tutti nella speranza che i nuovi token verrebbero creati quando l'utente ha riaperto l'app. Fin qui tutto bene.

Apple interromperà tutti i tentativi immediati di inviare una notifica push se si imbatte in un token APN errato.

Ho visualizzato esattamente lo stesso messaggio che non avevo mai visto prima (sotto) su varie app quindi sono contento di essere stato in grado di risolverlo.

Warning: fwrite() [function.fwrite]: SSL operation failed with code 1. OpenSSL Error messages: error:1409F07F:SSL routines:SSL3_WRITE_PENDING:bad write retry in PATH_TO_SCRIPT.php on line [NUMBER]

1

La soluzione è:

$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload; 
try { 
    $result = fwrite($fp, $msg, strlen($msg)); 
} catch (Exception $ex) { 
    sleep(1); //sleep for 5 seconds 
    $result = fwrite($fp, $msg, strlen($msg)); 
} 
Problemi correlati