我正在循环向PHP中的多个设备发送APNS。
while($row = mysql_fetch_array($result))
{
$row['devicetoken'];
$row['devcertificate'];
$row['prodcertificate'];
if($devprod=="dev"){
$apnsserverurl="ssl://gateway.sandbox.push.apple.com:2195";
$certificatename=$appname."".$row['devcertificate'];
}
elseif($devprod=="prod"){
$apnsserverurl="ssl://gateway.push.apple.com:2195";
$certificatename=$appname."".$row['prodcertificate'];
}
sendpush($row['devicetoken'],$certificatename,$apnsserverurl);
}
这是发送推送功能:
function sendpush($deviceToken,$certificatename,$apnsserverurl){
// Get the parameters from http get or from command line
$message = utf8_encode(urldecode($_POST['message']));
$badge = 0;
$sound = "";
// Construct the notification payload
$body = array();
$body['aps'] = array('alert' => $message);
if ($badge)
$body['aps']['badge'] = $badge;
if ($sound)
$body['aps']['sound'] = $sound;
/* End of Configurable Items */
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $certificatename);
$fp = stream_socket_client($apnsserverurl, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
//print "Failed to connect $err";
return;
}
else {
//print "Connection OK";
}
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
print "sending message :" . $payload . "n";
fwrite($fp, $msg);
fclose($fp);
}
面临的问题是它花费了太多时间。如何优化代码?
我终于使用了 https://code.google.com/p/apns-php/,它只建立 1 个连接并将消息排队
APNs 最佳实践要求您保持与其服务器的连接处于打开状态:
您可以与同一网关或多个网关实例建立多个连接。如果您需要发送大量推送通知,请将它们分散到多个不同网关的连接中。与使用单个连接相比,这可以提高性能:它可以让您更快地发送推送通知,并让 APNs 更快地交付推送通知。
通过多个通知保持与 APNs 的连接处于打开状态;不要重复打开和关闭连接。APNs 将快速连接和断开连接视为拒绝服务攻击。您应该使连接保持打开状态,除非您知道它将在较长时间内处于空闲状态,例如,如果您每天只向用户发送一次通知,则可以每天使用新连接。
源
Apple 可能会将您的重复连接视为 DoS 攻击并限制处理。
hylander0 提出了一个有效的观点。否则,您的代码看起来正常。您遇到的唯一问题是您要为发送的每次推送重新创建 SSL 连接。即使Apple不阻止您,仅该过程也需要时间。
将 stream_context_create(( 代码移到 sendPush 函数之外,让它成为全局代码,或者通过引用传递给循环中的 sendPush 函数(基本上应该只使用 fwrite((。这将打开与Apple的一个连接,并在可能不到一秒钟的时间内发送所有推送通知。
请注意,如果有效负载或推送令牌无效,则连接将被切断,因此,如果 fwrite 失败,请务必在继续循环之前重新创建连接。
是的,stream_socket_client(( 应该在循环的一侧。只保持 fwrite(( 在循环中。
一种使用 php 发送数千甚至数百万个 APNS 的简单方法(在 PHP 7 和 HTTP2 procotol 上测试(:
$data = array("aps" => $contents); // $contents if your data like badge, alert, etc
$payload = json_encode($data);
$header = ["alg" => "ES256", "kid" => "YOUAPIKEY"];
$header = base64_encode(json_encode($header));
$claim = ["iss" => $teamId, "iat" => time()];
$claim = base64_encode(json_encode($claim));
$token = $header.".".$claim;
$pkey = file_get_contents("YOUKEY.p8");
$signature = "";
openssl_sign($token, $signature, $pkey, 'sha256');
$sign = base64_encode($signature);
$jws = $token.".".$sign;
// headers
$headers = array(
"apns-topic: "."YOUBUNDLEID",
'Authorization: bearer ' . $jws,
"apns-priority: 10",
"apns-push-type: alert"
);
$devices = []; // YOUR array of tokens
$mh = curl_multi_init();
if (defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 5);
}
curl_multi_setopt($mh, CURLMOPT_PIPELINING, 2);
foreach($devices as $token)
{
// open connection
$http2ch = curl_init();
// cleanup device tokens
$token = str_replace(' ', '', trim($token, '<> '));
// url (endpoint)
$url = $http2_server."/3/device/".$token;
// other curl options
curl_setopt_array($http2ch, array(
CURLOPT_URL => $url,
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HEADER => 1,
CURLOPT_HTTP_VERSION => 3,
));
curl_multi_add_handle($mh,$http2ch);
}
$still_running = true;
do {
while (($curlCode = curl_multi_exec($mh, $still_running)) == CURLM_CALL_MULTI_PERFORM) {
curl_multi_select($mh);
}
if ($curlCode != CURLM_OK) {
break;
}
while ($res = curl_multi_info_read($mh)) {
$handle = $done['handle'];
$statusCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
$datas = curl_getinfo($handle);
// retrieve data like token to clean DB for example if error
curl_multi_remove_handle($mh, $handle);
}
} while ($still_running);
curl_multi_close($mh);