tcp客户端主动断开链接后,IOCP服务器会触发两次GetQueuedCompletionStatus()?

   最近用网CSDN学院中李军老师的IOCP代码做了一个服务端,但是在后期测试的时候遇到了如下问题:

自己做了一个客户端,主动往IOCP服务器发送报文(服务器不回复),一切正常,客户端主动断开链接也没啥问题。
但是我又在服务端增加了一个功能,将收到的客户端发来的报文原封不动的再回复给客户端,回复后客户端也能够正常收到,但是当客户端主动断开链接以后,IOCP的GetQueuedCompletionStatus会收到两次请求,
GetQueuedCompletionStatus(
In HANDLE CompletionPort,
Out LPDWORD lpNumberOfBytesTransferred,
Out PULONG_PTR lpCompletionKey,
Out LPOVERLAPPED* lpOverlapped,
In DWORD dwMilliseconds
);
并且第二个参数lpNumberOfBytesTransferred都为0,我想知道为啥客户端主动断开后会触发两次GetQueuedCompletionStatus,导致粗体部门会执行两次导致程序崩溃(如果工作线程数设置为1 则不会出现奔溃,如IOCP服务器不只接收不回复报文也不会崩溃)。工作线程代码如下:


DWORD WINAPI ServerWorkerThread(LPVOID p)
{
    assert(p);
    auto workers = (Workers*)p;
    workers->ThreadProc();
    return 0;
}

Workers::Workers(IocpServer* server)
{
    _iocpServer = const_cast<IocpServer*>(server);
}

void Workers::Start()
{
    SYSTEM_INFO SystemInfo;
    GetSystemInfo(&SystemInfo);

    // Create worker threads based on the number of processors available on the
    // system. Create two worker threads for each processor.

    for (int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
    {
        HANDLE ThreadHandle;

        // Create a server worker thread and pass the completion port to the thread.
        DWORD ThreadID;
        if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, this,
            0, &ThreadID)) == NULL)
        {
            printf("CreateThread() failed with error %d\n", GetLastError());
            return;
        }

        // Close the thread handle
        CloseHandle(ThreadHandle);
    }

    _iocpServer->Accept();
}

void Workers::ThreadProc()
{
    DWORD bytes_transferred;
    ULONG_PTR completion_key;
    DWORD Flags = 0;
    Overlapped* overlapped = NULL;

    while (1)
    {
        BOOL bRet = GetQueuedCompletionStatus(_iocpServer->_completion_port,
            &bytes_transferred,
            &completion_key,
            reinterpret_cast<LPOVERLAPPED*>(&overlapped),
            INFINITE);
        if (bRet == FALSE)
        {
            if (GetLastError() == WAIT_TIMEOUT || GetLastError() == ERROR_NETNAME_DELETED)
            {
                if (overlapped != NULL)
                {
                    if (overlapped->connection != NULL)
                    {
                        //客户端断开,调用回调函数
                        //WriteStrLog(overlapped, 2);
                        if (_iocpServer->OnDisconnectedUnusual)
                            _iocpServer->OnDisconnectedUnusual(overlapped->connection);
                        //CancelIo((HANDLE)overlapped->connection->GetSocket());
                        delete overlapped->connection;
                        continue;
                    }
                }
            }
        }
        else
        { 
            if (overlapped->type == Overlapped::Accept_type)
            {
                _iocpServer->Accept();
                if (_iocpServer->OnConnected)
                    _iocpServer->OnConnected(overlapped->connection);
                //WriteStrLog(overlapped,0);
                continue;
            }
            if (bytes_transferred <= 0)
            {
                if (overlapped->connection != NULL)
                {
                    //客户端正常断开
                    if (_iocpServer->OnDisconnected)
                        _iocpServer->OnDisconnected(overlapped->connection);
                    //bool br=CancelIoEx((HANDLE)overlapped->connection->GetSocket(), &overlapped->overlapped);
                    delete overlapped->connection;
                }
                continue;
            }

            if (overlapped->type == Overlapped::Type::Read_type)
            {
                // 异步读完成
                if (_iocpServer->OnRead)
                {
                    _iocpServer->OnRead(overlapped->connection, overlapped->connection->GetReadBuffer(), bytes_transferred);
                }
                continue;
            }

            if (overlapped->type == Overlapped::Type::Write_type)
            {
                auto conn = overlapped->connection;
                conn->SetSentBytes(conn->GetSentBytes() + bytes_transferred);

                //判断是否只发送了一部分
                if (conn->GetSentBytes() < conn->GetTotalBytes())
                {
                    //将剩余部分再发送
                    overlapped->wsa_buf.len = conn->GetTotalBytes() - conn->GetSentBytes();
                    overlapped->wsa_buf.buf = reinterpret_cast<CHAR*>(conn->GetWriteBuffer()) + conn->GetSentBytes();

                    auto send_result = WSASend(conn->GetSocket(),
                        &overlapped->wsa_buf, 1, &bytes_transferred,
                        0, reinterpret_cast<LPWSAOVERLAPPED>(overlapped),
                        NULL);

                    if (!(send_result == NULL || (send_result == SOCKET_ERROR && WSAGetLastError() == WSA_IO_PENDING)))
                        fprintf(stderr, "发送数据失败\n");
                }
                else
                {
                    //全部字节发送完成,此时再投递一个写操作
                    //AsyncRead(overlapped->connection);
                    if (_iocpServer->OnWrite)
                        _iocpServer->OnWrite(overlapped->connection, bytes_transferred);
                }
            }
        }
    }
}

你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答


本次提问扣除的有问必答次数,将会以问答VIP体验卡(1次有问必答机会、商城购买实体图书享受95折优惠)的形式为您补发到账户。


因为有问必答VIP体验卡有效期仅有1天,您在需要使用的时候【私信】联系我,我会为您补发。