Соединение с сервером Poco C++ Websocket сбрасывается одноранговым узлом

Я пишу своего рода приложение для чат-сервера, в котором сообщение, полученное от одного клиента веб-сокета, отправляется всем другим клиентам веб-сокета. Для этого я держу подключенных клиентов в списке. Когда клиент отключается, мне нужно удалить его из списка (чтобы будущие "отправки" не провалились).

Однако иногда, когда клиент отключается, сервер просто получает исключение «соединение сброшено узлом», и код не получает шанса удалить из списка клиентов. Есть ли способ гарантировать «хорошее» уведомление о том, что соединение было сброшено?

Мой код:

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
    }

}

person user2776917    schedule 18.11.2013    source источник
comment
Вы когда-нибудь находили решение своей проблемы? Я также получаю сброс соединения по узлу, когда клиент отключается.   -  person Dánjal Salberg Adlersson    schedule 10.12.2015


Ответы (5)


См. ReceiveFrame() документацию:

Возвращает количество полученных байтов. Возвращаемое значение 0 означает, что одноранговый узел отключил или закрыл соединение.

Поэтому, если вызов receiveFrame() возвращает ноль, вы можете действовать соответствующим образом.

person Alex    schedule 11.12.2015

Я не знаю, является ли это ответом на вопрос, но реализованная вами реализация не связана с кадрами PING. В настоящее время (начиная с моей версии POCO: 1.7.5) инфраструктура POCO не делает этого автоматически. Недавно я задал вопрос об этом. Согласно RFC (6465), кадры ping и pong используются (среди прочего) в качестве функции поддержки активности. Поэтому это может иметь решающее значение для обеспечения стабильного соединения с течением времени. Большая часть этого является догадкой с моей стороны, поскольку я сам сейчас экспериментирую с этим.

@ Алекс, я думаю, вы главный разработчик POCO, комментарий к моему ответу был бы очень признателен.

person Stefan Karlsson    schedule 27.01.2017
comment
Сообщается об этой проблеме: github.com/pocoproject/poco/issues/2351 - person Dawid Drozd; 26.06.2019

Я расширил уловку, чтобы выполнить некоторую обработку исключений для «Сброс соединения одноранговым узлом».

catch (Poco::Net::WebSocketException& exc)
{
// Do something
}
catch (Poco::Exception& e)
{
// This is where the "Connection reset by peer" lands
}
person Dánjal Salberg Adlersson    schedule 10.12.2015

Немного опоздал на вечеринку здесь ... но я также использую Poco и Websockets - и правильная обработка отключений была сложной.

В итоге я сам реализовал простую функцию ping, когда клиентская сторона отправляет сообщение ACK для каждого полученного кадра WS. Отдельный поток на стороне сервера пытается прочитать сообщения ACK, и теперь он определяет, когда клиент отключился, просматривая 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");
});

на стороне клиента я просто отправляю фиктивное сообщение «ACK» обратно на сервер (JS) каждый раз, когда получаю кадр WS с сервера (POCO).

websocket.onmessage = (evt) => {
    _this.receivedData = JSON.parse(evt.data);
    websocket.send("ACK");
};
person Njål Arne Gjermundshaug    schedule 13.05.2019

Речь идет не об обработке разъединения, а о стабильности соединения. Были некоторые проблемы с сервером POCO Websocket в режиме StreamSocket и клиентом C#. Иногда клиент отправляет сообщения Pong с полезной нагрузкой нулевой длины, и происходит отключение, поэтому я добавил код обработки Ping и Pong.

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);
    }
}
person Anton    schedule 30.06.2021