IOCP服务器可用于WebSocket连接。当浏览器发送关闭帧时,服务器delete
是该客户端,closesocket
函数正在调用客户端的对象析构函数。但是,即使在关闭套接字后,GetQueuedCompletionStatus
函数仍继续从该套接字中选择事件。当然,结果是false
和0字节被传输,但是客户端ptr和OVERLAPPED
ptr不为NULL,并且GetLastError
返回1236(ERROR_CONNECTION_ABORTED)...所以是的,它被中止了, closesocket
被称为...但是为什么它仍在这里???以及如何停止接收此“无用”事件?我可以在thead的循环中调用continue
,但是如果函数永远选择此已删除的客户端,则会浪费CPU时间。
这是工作线程循环的一部分:
while(WAIT_OBJECT_0 != WaitForSingleObject(EventShutdown, 0)){
DWORD BytesTransfered = 0;
OVERLAPPED *asyncinfo = nullptr;
client *Client = nullptr;
BOOL QCS = GetQueuedCompletionStatus(hIOCP, &BytesTransfered, (PULONG_PTR)&Client, &asyncinfo, INFINITE);
if(!Client ) break;
switch( QCS * (BytesTransfered > 0) * Client->OpCode() ){
case OP_TYPE_RECV:{
.....
switch( recv_buf[0] &0xFF ){
....
case FIN_CLOSE:
printf("FIN_CLOSE on client %u\n", Client->Socket());
default:{
RemoveClient(Client);
break;
}
}
}
case OP_TYPE_SEND:{
...
}
default:{
printf("Client %u (%lu bytes transferred, QCS is %d)\n", Client->Socket(), BytesTransfered, QCS);
break;
}
客户的析构函数:
client::~client(){
while(!HasOverlappedIoCompleted(&asyncinfo)) Sleep(0);
closesocket(socket);
if( a_ctx ) delete a_ctx;
if( q_ctx ) delete q_ctx;
delete [] data_buffer;
printf("Client %u deleted\n", socket);
}
...和服务器日志:
来自127.0.0.1的客户端296(代理1987)
来自127.0.0.1的客户端308 (主管)
来自324.0.1.0.1的客户端324
总数:3 客户端
向324发送33278字节
324发送完成
发送完成308个
发送40529字节到324
发送完成 为324
向324发送41128字节
为324发送完成 将40430字节发送到324
发送完成,完成324
FIN_CLOSE 客户端324
客户端324已删除
客户端324(已传输0字节, QCS为0)
客户端324(已传输0字节,QCS为0)
客户端 324(已传输0个字节,QCS为0)
客户端324(0个字节 已传输,QCS为0)
客户端324(已传输0字节,QCS为 0)
看到“ 324(已传输0个字节,QCS为0)”吗?插座324已关闭。为什么在析构函数的消息“ 客户端324已删除”之后发生?
答案 0 :(得分:1)
我认为您没有包含足够的代码来获得完整的图片,但是此行看起来很可疑:
switch( QCS * (BytesTransfered > 0) * Client->OpCode() ){
这有两个原因。首先,不能保证GetQueuedCompletionStatus
成功返回1。 MSDN仅承诺它将返回非零。因此,为成功案例依赖特定的值是有风险的。其次,您无法区分失败的调用和返回0字节的成功调用。您应该真正区分用于管理出队和分配特定I / O事件的逻辑。这将使您的代码更易于理解和维护。
您还必须记住,每个插槽都有两个侧面。在用户空间中有与之关联的结构和套接字句柄,然后还有管理低级详细信息的内核对象。仅仅因为在用户端关闭了句柄并不意味着内核对象就消失了。内核对象被引用计数,并且通常会持续存在,直到涉及这些对象的所有I / O完成为止。
这就是为什么从程序角度来看,在套接字被“破坏”之后,仍然可以获得套接字的I / O通知的原因。特别是对于套接字,关闭序列将在您关闭句柄之后 进行(因为在此之前您没有明确关闭套接字)。
代替响应特定消息而销毁Client对象,只需关闭套接字句柄并清理其他结构以响应中止通知即可。您也可以考虑进行正常的断开连接,而不是中止连接。