在过去的几天里,当我们使用套接字连接到生产服务器上的APN服务器时,我们在PHP中遇到了一些奇怪的行为。
在大多数情况下,有效负载被推送而没有任何错误,并且客户端接收到通知。然而,在某些情况下,我们开始收到PHP错误(即使我们收到错误,有时也会推送通知)。当我们开始看到这个错误时,它会持续几个小时,然后消失,PHP会像什么都没发生一样继续工作。
另一件奇怪的事情是,从shell运行相同的PHP代码不会产生任何错误。从web(nginx/php-fpm)运行它确实。。。运行在shell和web上的PHP有相同的配置,共享相同的PHP.ini。唯一的区别是web运行在PHP-fpm上。
此外,相同的代码+证书在我们的临时服务器上运行,没有任何错误。生产服务器是临时服务器的副本,所以每个配置都是相同的。
我们能够找到一些可能导致此错误的答案,包括来自stackoverflow.com的答案,但我们无法找到解决方案。
向苹果服务器发送的通知是逐个发送的,而不是捆绑发送。但我们并没有建立太多的联系(也许一天一千个)。没有队列系统。
因此,简而言之,
- 我们有时会在发送通知时收到PHP错误,但并非总是如此
- 通过相同的PHP从shell发送通知不会产生任何错误
- 从临时服务器发送通知不会产生任何错误
我们试过这些
- 正在重新创建证书和密钥
- 重新创建PEM文件
- 将ssl://更改为sslv3://
- 使用stream_socket_client
- 使用fsockopen
- 更改/删除证书密码
错误为:
2012/08/28 12:18:09 [error] 4282#0: *225858 FastCGI sent in stderr:
"PHP message: PHP Warning: fwrite() [<a href='function.fwrite'>function.fwrite</a>]:
SSL operation failed with code 1. OpenSSL Error messages:
error:1409F07F:SSL routines:func(159):reason(127) in
/usr/local/nginx/html/play/classes/PushNotification.php on line 283"
while reading response header from upstream, client: 94.---.---.---,
server: play.--------.com, request: "POST /game_request_random.php HTTP/1.1",
upstream: "fastcgi://unix:/var/run/phpfpm.sock:",
host: "play.--------.com", referrer: "http://--------.com/"
从php连接和发送有效载荷的代码实际上是一个类的一部分,这部分是建立连接和发送载荷的部分:
private function ConnectAndSend ( $msg = false ) {
$ctx = stream_context_create();
stream_context_set_option( $ctx, 'ssl', 'local_cert', $this->certificate );
stream_context_set_option( $ctx, 'ssl', 'passphrase', $this->certificatepass );
// Open a connection to the APNS server
$fp = stream_socket_client( APN_SERVER, $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx );
if ( !$fp ) {
errorlog( "Push notification error : $err $errstr" );
$this->error = "$err $errstr";
return;
}
// Build the notification
if ( !$msg ) {
$msg = chr( 0 ) . pack( 'n', 32 ) . pack( 'H*', $this->devicetoken ) . pack( 'n', strlen( $this->payload ) ) . $this->payload;
}
// Send it to the server
if ( !($result = fwrite( $fp, $msg, strlen( $msg ) )) ) {
// Could not send
$this->error = 'Notification could not be send';
errorlog( "Push notification error : {$this->error}" );
} else {
// Notification sent
$this->error = false;
errorlog( "Push notification sent" );
}
fclose($fp);
// Reset the content
$this->devicetoken = false;
$this->message = false;
$this->command = false;
$this->badge = 0;
$this->payload = false;
$this->sound = false;
}
- stream_socket_connection是错误消息中出现的第283行
- 我们没有使用沙箱(sslv3://gateway.push.apple.com:2195)
- PHP版本为5.3.15
这是我们不知道的PHP或OpenSSL错误吗?你知道在哪里检查什么吗?苹果有一个网站可以让我们检查APN网络的当前健康状况吗?
非常感谢您的帮助。。。感谢
查看此代码以发送多条消息
$i = 0;
while($res = mysql_fetch_array( $result )) {
$deviceTokens[$i] = $res['token'];
$i++;
}
// APNs Push testen auf Token
//$deviceToken = $token; // Hier das Device-Token angeben, ist 64-stellig
// Payload erstellen und JSON codieren
$message = $_POST['message'];
$message = utf8_encode($message);
$payload['aps'] = array('alert' => 'Neuer Artikel in Aktuelles', 'badge' => +1, 'sound' => 'default');
if (trim($message) != '') {
$payload['aps'] = array('alert' => "$message", 'badge' => 1, 'sound' => 'default');
}
$payload = json_encode($payload);
//Development: $apnsHost = 'gateway.sandbox.push.apple.com';
$apnsHost = 'gateway.push.apple.com';
$apnsPort = 2195;
//Development: $apnsCert = 'apsDevBundle.pem';
$apnsCert = 'apns-dev.pem';
// Stream erstellen
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
if ($error==0)
{
for($i = 0; $i<count($deviceTokens); $i++) {
// Build the binary notification
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceTokens[$i])) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
}
// Verbindung schliessen
fclose($apns);
}
else
{
var_dump($error);
var_dump($errorString);
die("Fehler aufgetreten.");
}