我正在编写一种聊天服务器应用程序,其中从一个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
}
}
答案 0 :(得分:1)
答案 1 :(得分:1)
我不知道这是否是问题的答案,但你所做的实现并不涉及PING帧。目前(截至我的POCO版本:1.7.5)这不是由POCO框架自动完成的。我提出question about that recently。根据RFC(6465),使用ping和pong帧(以及其他)作为保持活动功能。因此,为了使您的连接随时间保持稳定,这可能是正确的。其中大部分都是我自己的猜测,因为我现在正在尝试这个。
@Alex,你是POCO的主要开发者我相信,对我的回答的评论将不胜感激。
答案 2 :(得分:0)
我扩展了catch,为“Connection by peer”做了一些异常处理。
catch (Poco::Net::WebSocketException& exc)
{
// Do something
}
catch (Poco::Exception& e)
{
// This is where the "Connection reset by peer" lands
}
答案 3 :(得分:0)
在这里参加聚会有点晚了...但是我也在使用Poco和Websockets-正确处理断开连接很棘手。
我最终自己实现了一个简单的ping功能,其中客户端针对接收到的每个WS Frame发送一个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");
};
答案 4 :(得分:0)
这不是关于断开连接的处理,而是关于连接的稳定性。 在 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);
}
}