【发布时间】:2013-08-25 01:42:11
【问题描述】:
编辑 - 使用增强的二进制格式
原来我没有使用增强型二进制格式,所以我更改了代码。
<?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> Identifier:<b>' . $error_response['identifier'] . '</b> 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;
}
?>
使用此新代码时,由于此错误,我仍然无法发送超过 300 条以上的消息:
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
仅发送几条推送消息时,此代码可以正常工作。
简单二进制格式的旧问题 所以我很久以前就集成了推送通知,它对于发送给少于 500 人的消息效果很好。现在我正在尝试向 1000 多人发送推送通知,但随后我收到了损坏的错误
Warning: fwrite() [function.fwrite]: SSL: Broken pipe in PATH_TO.PHP on line x
我已经阅读了苹果文档,并且知道无效令牌会导致套接字断开连接。一些在线解决方案建议像这样检测断开连接并重新连接:
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.
我还在运行一个反馈服务,它检测无效令牌(想要推送通知但删除了应用程序的用户)并且工作正常。该 php 脚本回显已删除的 ID,我可以确认这些令牌已从我们的 MySQL 数据库中删除。
我怎样才能检测到断开或损坏的管道并对此做出反应,以便我的推送通知可以覆盖 1000 多人?
目前我正在使用这个简单的 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);
}
?>
当发生 SLL Broken Pipe 错误时,fwrite 也会返回 0 个写入字节。
我还必须提到,我不是 PHP 或 Web 开发人员,而是一名应用程序开发人员,所以我的 php 技能不是那么好。
【问题讨论】:
-
您使用的是简单的二进制格式,它不会返回响应。您应该使用增强格式以获得错误响应。有关详细信息,请参阅this answer。
-
谢谢。我现在使用的是增强格式,它在发送大量消息时返回此错误(而不是仅发送几条消息):警告:fwrite() [function.fwrite]:SSL 操作失败,代码为 1。OpenSSL 错误消息:错误:1409F07F:SSL 例程:SSL3_WRITE_PENDING:PATH_TO_PHP_FILE_ON_LINE 中的错误写入重试
-
看看this question,有用吗?
-
本质上说,当您发送时,您可能会遇到写入错误。在这种情况下,您必须使用相同的参数进行相同的写入操作,否则您将收到错误 1409F07F。在此错误之前,您是否有
checkAppleErrorResponse($fp);函数回显的内容?我看到你在循环内调用它,但你没有检查它的返回值。如果该函数打印错误,您应该管理它并再次执行 fwrite,然后再转到下一项 -
或者,更好的是,检查 fwrite 函数写入的字节数。如果为0,则有错误,必须重新进行写操作
标签: php push-notification apple-push-notifications apns-php