【问题标题】:Poco c++ Websocket server connection reset by peerPoco c++ Websocket服务器连接由对等方重置
【发布时间】:2013-11-18 12:15:46
【问题描述】:

我正在编写一种聊天服务器应用程序,其中从一个 websocket 客户端接收到的消息被发送到所有其他 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
    }

}

【问题讨论】:

  • 您找到解决问题的方法了吗?当客户端断开连接时,我还会收到“对等连接重置”。

标签: websocket poco-libraries


【解决方案1】:

见receiveFrame() documentation:

返回接收到的字节数。返回值为 0 表示对端已关闭或关闭连接。

所以如果receiveFrame()调用返回0,你就可以采取相应的行动了。

【讨论】:

    【解决方案2】:

    我不知道这是否是问题的答案,但您所做的实现不处理 PING 帧。目前(截至我的 POCO 版本:1.7.5)不是由 POCO 框架自动完成的。我提出了question about that recently。根据 RFC (6465),ping 和 pong 帧(除其他外)用作保活功能。因此,为了使您的连接随着时间的推移保持稳定,这可能是至关重要的。这大部分都是我自己的猜测,因为我现在自己也在做这个实验。

    @Alex,我相信您是 POCO 的主要开发人员,非常感谢您对我的回答发表评论。

    【讨论】:

    【解决方案3】:

    我扩展了catch,对“Connection reset by peer”做一些异常处理。

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

    【讨论】:

      【解决方案4】:

      在这里聚会有点晚了......但我也在使用 Poco 和 Websockets - 并且正确处理断开连接很棘手。

      我最终自己实现了一个简单的 ping 功能,客户端为它接收到的每个 WS 帧发送一个 ACK​​ 消息。服务器端的一个单独线程尝试读取 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");
      });
      

      在客户端,每次我从服务器 (POCO) 收到一个 WS 帧时,我都会向服务器 (JS) 发送一个虚拟的“ACK”消息。

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

      【讨论】:

        【解决方案5】:

        这不是关于断开连接的处理,而是关于连接的稳定性。 StreamSocket 模式下的 POCO Websocket 服务器和 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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2013-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多