2
votes

Iam using Ratchet Websocket Library for chatting on mobile application.

I have used following code: To Send / Recive Message:

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();
    }
}

On iOS when user enters the chat screen I call Connection Open to open a connection between mobile and web. But When I disconnect Wifi. On server OnClose didn't get called. So Server didn't get notified that the current connection is disconnected. And when server send any message to application it doesn't get received by the iOS app. I want to know is there any way to detect in OnMessage function that $client->send($msg) is actually sent or not?

1

1 Answers

1
votes

What you have to remember is that a WebSocket is just a TCP socket connection. Which is a stateful socket that can be terminated at either end.

The connection termination phase uses a four-way handshake, with each side of the connection terminating independently. When an endpoint wishes to stop its half of the connection, it transmits a FIN packet, which the other end acknowledges with an ACK. Therefore, a typical tear-down requires a pair of FIN and ACK segments from each TCP endpoint.

Source: Wikipedia - Transmission Control Protocol: 4.2 Connection Termination

So there are two ways that this socket can be terminated.

  1. Cleanly by either end sending a FIN packet and the opposite end acknowledging that request, then completing the closing handshake.
  2. Not So Cleanly by one end being cut off from the socket without sending a FIN packet to complete the closing handshake.

In your case, disconnecting the WiFi means the server has likely not received any such closing handshake from the client and will remain in a TIME_WAIT state for the maximum allowed timeout. Until this timeout is reached, the server has no idea that the connection was lost. Unless the server attempts to send data to this client, in which case a connection lost error will occur, which is also subject to a timeout, because the packets will be sent multiple times without acknowledgement from the other end, before it is determined that the client is definitely gone.

You can't detect this from your onMessage callback, but you can detect it from your onError callback method, however. When it so happens that the server tries to send data to the client and fails, or a timeout occurs, the Exception will bubble up to your application code from this callback where you can catch it and handle it appropriately.

Just remember, because the unclean disconnect from the client, this exception won't show up until the timeout actually occurs. So it doesn't happen instantly, because the server won't know about it instantly.