1

I am writing a kind of chat server app where a message received from one websocket client is sent out to all other websocket clients. To do this, I keep the connected clients in a list. When a client disconnects, I need to remove it from the list (so that future "sends" do not fail).

However, sometimes when a client disconnects, the server just gets an exception "connection reset by peer", and the code does not get chance to remove from the client list. Is there a way to guarantee a "nice" notification that the connection has been reset?

My code is:

void WsRequestHandler::handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp)
{
    int             n;
    Poco::Timespan  timeOut(5,0);

    try
    {
        req.set("Connection","Upgrade"); // knock out any extra tokens firefox may send such as "keep-alive"
        ws = new WebSocket(req, resp);
        ws->setKeepAlive(false);
        connectedSockets->push_back(this);

        do
        {
            flags = 0;
            if (!ws->poll(timeOut,Poco::Net::Socket::SELECT_READ || Poco::Net::Socket::SELECT_ERROR))
            {
//                  cout << ".";
            }
            else
            {
                n = ws->receiveFrame(buffer, sizeof(buffer), flags);
                if (n > 0)
                {
                    if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_BINARY)
                    {
                        // process and send out to all other clients
                        DoReceived(ws, buffer, n);
                    }
                }
            }
        }
        while ((flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
        // client has closed, so remove from list
        for (vector<WsRequestHandler *>::iterator it = connectedSockets->begin() ; it != connectedSockets->end(); ++it)
        {
            if (*it == this)
            {
                connectedSockets->erase(it);
                logger->information("Connection closed %s", ws->peerAddress().toString());
                break;
            }
        }
        delete(ws);
        ws = NULL;
    }
    catch (WebSocketException& exc)
    {
    //never gets called
    }

}
user2776917
  • 21
  • 1
  • 4

5 Answers5

2

See receiveFrame() documentation:

Returns the number of bytes received. A return value of 0 means that the peer has shut down or closed the connection.

So if receiveFrame() call returns zero, you can act acordingly.

Community
  • 1
  • 1
Alex
  • 5,159
  • 4
  • 25
  • 33
2

I do not know if this is an answer to the question, but the implementation you have done does not deal with PING frames. This is currently (as of my POCO version: 1.7.5) not done automatically by the POCO framework. I put up a question about that recently. According to the RFC (6465), the ping and pong frames are used (among others) as a keep-alive function. This may therefore be critical to get right in order to get your connection stable over time. Much of this is guess-work from my side as I am experimenting with this now myself.

@Alex, you are a main developer of POCO I believe, a comment on my answer would be much appreciated.

Community
  • 1
  • 1
Stefan Karlsson
  • 1,092
  • 9
  • 21
0

I extended the catch, to do some exception handling for "Connection reset by peer".

catch (Poco::Net::WebSocketException& exc)
{
// Do something
}
catch (Poco::Exception& e)
{
// This is where the "Connection reset by peer" lands
}
0

A bit late to the party here... but I am using Poco and Websockets as well - and properly handling disconnects was tricky.

I ended up implementing a simple ping functionality myself where the client side sends an ACK message for every WS Frame it receives. A separate thread on the server side tries to read the ACK messages - and it will now detect when the client has disconnected by looking at flags | WebSocket::FRAME_OP_CLOSE.

//Serverside - POCO. Start thread for receiving ACK packages. Needed in order to detect when websocket is closed!
thread t0([&]()->void{
    while((!KillFlag && ws!= nullptr && flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE && machineConnection != nullptr){
        try{
            if(ws == nullptr){
                return;
            }
            if(ws->available() > 0){
                int len = ws->receiveFrame(buffer, sizeof(buffer), flags);
            }
            else{
                Util::Sleep(10);
            }
        }
        catch(Poco::Exception &pex){
            flags = flags | WebSocket::FRAME_OP_CLOSE;
            return;
        }
        catch(...){
            //log::info(string("Unknown exception in ACK Thread drained"));
            return;
        }
    }
    log::debug("OperatorWebHandler::HttpRequestHandler() Websocket Acking thread DONE");
});

on the client side I just send a dummy "ACK" message back to the server (JS) every time I receive a WS frame from the server (POCO).

websocket.onmessage = (evt) => {
    _this.receivedData = JSON.parse(evt.data);
    websocket.send("ACK");
};
0

It is not about disconnect handling, rather about the stability of the connection. Had some issues with POCO Websocket server in StreamSocket mode and C# client. Sometimes the client sends Pong messages with zero length payload and disconnect occurs so I added Ping and Pong handling code.

int WebSocketImpl::receiveBytes(void* buffer, int length, int)
{
    char mask[4];
    bool useMask;
    _frameFlags = 0;
    for (;;) {
        int payloadLength = receiveHeader(mask, useMask);
        int frameOp = _frameFlags & WebSocket::FRAME_OP_BITMASK;
        if (frameOp == WebSocket::FRAME_OP_PONG || frameOp == 
            WebSocket::FRAME_OP_PING) {
            std::vector<char> tmp(payloadLength);
            if (payloadLength != 0) {
                receivePayload(tmp.data(), payloadLength, mask, useMask);
            }
            if (frameOp == WebSocket::FRAME_OP_PING) {
                sendBytes(tmp.data(), payloadLength, WebSocket::FRAME_OP_PONG);
            }
            continue;
        }
        if (payloadLength <= 0)
            return payloadLength;
        if (payloadLength > length)
            throw WebSocketException(Poco::format("Insufficient buffer for 
                      payload size %d", payloadLength), 
                      WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
        return receivePayload(reinterpret_cast<char*>(buffer), payloadLength, 
                   mask, useMask);
    }
}
Anton
  • 29
  • 1
  • 6