代码之家  ›  专栏  ›  技术社区  ›  Iceman

GetQueuedCompletionStatus继续选择关闭套接字上的事件

  •  1
  • Iceman  · 技术社区  · 6 年前

    IOCP服务器使用WebSocket连接。当浏览器发送关闭帧时,服务器 delete 是这个客户吗? closesocket 函数正在调用客户端的对象析构函数。但即使在插座关闭后, GetQueuedCompletionStatus 函数继续从此套接字中选择事件。当然结果是 false 传输了0个字节,但客户端ptr和 OVERLAPPED ptr不为空,并且 GetLastError 返回1236(错误连接中断)所以是的,它被中止了, 关闭连接 被称为…但为什么它还在这里????如何停止接收这些“无用”事件?我可以打电话 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);
    }
    

    …服务器日志:

    客户296,127.0.0.1(代理1987)
    客户308来自127.0.0.1 (监督员)
    127.0.0.1中的客户324(主管)
    总数:3 客户端(S)
    发送33278字节到324
    发送完成324
    发送完成308
    发送40529字节到324
    发送完成 为324
    发送41128字节到324
    发送完成324
    发送40430字节到324
    发送完成324
    芬尼克洛顿 客户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已删除 “?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Peter Ruderman    6 年前

    我认为您没有包含足够的代码来获得完整的图片,但这一行看起来很可疑:

    switch( QCS * (BytesTransfered > 0) * Client->OpCode() ){
    

    这有几个问题。第一, GetQueuedCompletionStatus 不保证成功时返回1。msdn只承诺返回非零。因此,依赖成功案例的特定值是有风险的。其次,您无法区分失败的调用和返回0字节的成功调用。您应该真正地将管理出列和分派特定I/O事件的逻辑分开。这将使代码更容易理解和维护。

    您还必须记住,每个插座都有两面。在用户空间中有您与之关联的结构和套接字句柄,然后有管理低级细节的内核对象。仅仅因为关闭了用户端的句柄并不意味着内核对象会消失。内核对象被引用计数,通常会一直持续到涉及这些对象的所有I/O完成为止。

    这就是为什么从程序的角度来看,一个套接字被“销毁”之后,您仍然可以得到它的I/O通知。尤其是插座,关闭程序将发生。 之后 您已经关闭了句柄(因为在此之前没有显式关闭套接字)。

    不要响应特定的消息而破坏客户机对象,只需关闭套接字句柄并清理其他结构以响应中止通知。您还可以考虑执行优雅的断开连接,而不是中止连接。