发送 voip 推送通知后,是否应该关闭与 APNs 的连接?



我正在使用simplepush.php脚本在用户之间发送VoIP推送。 我的应用可能会发出许多这样的推送请求,具体取决于它获得的用户数量。 我发现的每个 simplepush.php 示例似乎都在最后显式关闭了连接 - 这是我的脚本(见最后一行):

$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'voip.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$fp = stream_socket_client($apnsUrl, $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
$body['info'] = array(
'roomname' => $roomName,
'uuid' => $uuid
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);

请注意:我正在使用旧的 APNs 二进制接口来发送通知而不是 HTTP/2 请求,因为所有 simplepush 脚本都使用它。我不精通 PHP,但似乎脚本在每次调用结束时都会关闭连接:fclose($fp);

但根据苹果的说法,我应该保持连接打开:

管理连接的最佳实践 通过多个通知保持与 APNs 的连接处于打开状态;不要重复打开和关闭连接。APNs 将快速连接和断开连接视为拒绝服务攻击。 https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW6

但是由于我使用的是传统的二进制接口,我实际上应该在每次调用后关闭连接吗? 还是我误解了这里的fclose($fp);功能? 使用此二进制文件时,任何关于处理连接的适当方法的明确说明将不胜感激!

解释

据我所知,不关闭每个通知的连接的建议来自批量通知传递区域,其中许多用户收到相同的通知。

除了可能被解释为攻击之外,一直关闭和重新打开效率非常低,会导致巨大的交付延迟。仅在流上写入二进制消息比打开和关闭每个通知要快得多。请参阅此示例:

// Open a connection to the apns server (this code is the same as a few lines below, so if changed here, also change there)
$stream_context = stream_context_create();
stream_context_set_option($stream_context, 'ssl', 'local_cert', 'voip.pem');
stream_context_set_option($stream_context, 'ssl', 'passphrase', 'secret_pass');
$apns = stream_socket_client($url, $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $stream_context);
// Return if connection is impossible
if (!$apns) { return; }
// Create payload body
$body['aps'] = array(
'alert' => $title,
'badge' => 1
);
// Encode the payload as json
$payload = json_encode($body);
// Iterate through token array
foreach($tokens as $token) {    
$user_id = 2 // Random id ;)
// Build binary notification
$msg = pack("C", 1);
$msg .= pack("N", $user_id);
$msg .= pack("N", $notification_expiration_date);
$msg .= pack("n", 32);
$msg .= pack('H*', $token);
$msg .= pack("n", strlen($payload));
$msg .= $payload;
// Send to the server
$fwrite_result = fwrite($apns, $msg, strlen($msg));
}
fclose($apns);

查看它如何打开一个连接,写入数组中的每个令牌,然后关闭,而不是每次打开、写入和关闭。

以批量推送应用程序为例,现在由您的设计决定是否应保留连接。如果您每小时发送一次通知,那么在我看来,为每个通知打开和关闭都是合适的。但是,如果吞吐量为每分钟多个通知,则需要保持连接打开。

建议

您可以采取的一种方法是查找在完成现有队列后要传递的任何新通知。如果没有,您可以再等待几分钟,然后再次检查并关闭连接(如果仍然没有新内容),如果有一些,请保持打开状态并使用相同的连接发送新通知。

这将符合苹果的建议:

除非知道连接将长时间处于空闲状态,否则应使连接保持打开状态,例如,如果您每天只向用户发送一次通知,则每天使用一个新连接是可以接受的做法。

警告

使用单个连接时要考虑的重要一件事是错误处理:无效令牌(生产/沙盒模式混淆)可能会导致连接在您不注意的情况下关闭,但还有其他帖子进一步讨论。

最新更新