我使用Ratchet Websocket Library在移动应用程序上聊天。
我使用了以下代码:发送/重发消息:
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct()
{
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg)
{
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
在iOS上,当用户进入聊天界面时,我会调用Connection Open来打开手机和网络之间的连接。但当我断开Wifi。在服务器上OnClose没有被调用。因此,服务器未收到有关当前连接断开连接的通知。当服务器向应用程序发送任何消息时,它不会被iOS应用程序接收。我想知道有没有办法在OnMessage
函数中检测到$client->send($msg)
实际上是否已被发送?
答案 0 :(得分:1)
你必须记住的是WebSocket只是一个TCP套接字连接。这是一个可以在任何一端终止的有状态套接字。
连接终止阶段使用四次握手,连接的每一侧都独立终止。当端点希望停止其连接的一半时,它发送FIN数据包,另一端用ACK确认。因此,典型的拆除需要来自每个TCP端点的一对FIN和ACK段。
Source: Wikipedia - Transmission Control Protocol: 4.2 Connection Termination
因此有两种方法可以终止此套接字。
FIN
数据包,另一端确认该请求,然后完成结束握手。FIN
数据包以完成结束握手。在您的情况下,断开WiFi意味着服务器可能没有从客户端收到任何此类结束握手,并将保持TIME_WAIT
状态以获得最大允许超时。在达到此超时之前,服务器不知道连接已丢失。除非服务器尝试向此客户端发送数据,否则将发生连接丢失错误,这也会发生超时,因为在确定数据包之前,数据包将在未经另一端确认的情况下多次发送客户肯定不见了。
您无法通过onMessage
回调检测到此问题,但是您可以通过onError
回调方法检测到它。如果发生这种情况,服务器尝试将数据发送到客户端并失败,或者发生超时,则异常将从此回调冒泡到您的应用程序代码,您可以在其中捕获并适当地处理它。
请记住,因为与客户端的不正常断开连接,在实际发生超时之前不会显示此异常。所以它不会立即发生,因为服务器不会立即知道它。