使用IOCP处理客户端的TCP连接时,遇到一个现象,接收到数据(客户端的登录消息)之后,处理完毕,回复了一个应答消息,客户端也收到了,这个消息的长度­假设是100字节。客户端也收到了,之后客户端并没有发送任何信息给服务器。但是服务器端的线程循环到GetQueuedCompletionStatus的地­方, 
if (bool bOK = GetQueuedCompletionStatus(m_CompletionPort, 
&BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData, 
INFINITE) == 0) 
居然又向下执行了,按说应该在此处挂住才对啊? 
检查各个参数,发现BytesTransferred的值是我刚才给客户端发送的数据的字节数,但是PerIoData里面也没有任何数据,全部用0xCD填充­的。 
给客户端发送数据的函数如下: 
void CWorkerThread::SendPacket(char* pBufHeader, char* pBufData, int 
nDataLen) 

        PER_IO_OPERATION_DATA * PerIoData = new PER_IO_OPERATION_DATA; 
        int iRtn = 0; 
        DWORD dwSendBytes, dwRecvBytes, dwFlags; 
        char * pBuffer = new char[512]; 
        memset(pBuffer, 0, 512); 
        memcpy(pBuffer, pBufHeader, MESSAGE_HEADER_LEN); 
        memcpy(pBuffer + MESSAGE_HEADER_LEN, pBufData, nDataLen); 
        ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); 
        PerIoData->DataBuf.buf = pBuffer; 
        PerIoData->DataBuf.len = MESSAGE_HEADER_LEN + nDataLen;         iRtn = WSASend(m_pPerHandleData->m_hSocket, &(PerIoData->DataBuf), 1, 
&dwSendBytes, 0, &(PerIoData->Overlapped), NULL); 
        if (iRtn == SOCKET_ERROR) 
        { 
                if (WSAGetLastError() != ERROR_IO_PENDING) 
                { 
                        TRACE("WSASend() failed with error %d\n", WSAGetLastError()); 
                } 
        } 

请问是怎么回事? 

解决方案 »

  1.   

    本来是发送出去的数据,怎么会导致本地的GetQueuedCompletionStatus(m_CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData, 
    INFINITE)调用返回呢? 
    云彩了! 
    哪位指点指点俺?先谢谢了 
      

  2.   

    你发送数据后,PER_IO_OPERATION_DATA 的操作改为了什么 ?如果没改的话,那它当然在循环了。
      

  3.   

    在send里面重新new了一个PerIoData,应该是GetQueuedCompletionStatus(m_CompletionPort, 
    &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData, 
    INFINITE) == 0) 参数里面的这个PerIOData才对吧。
      

  4.   

    你发送数据后,PER_IO_OPERATION_DATA 的操作改为了什么 ?如果没改的话,那它当然在循环了。
    请问到底是应该改还是不应该改?
      

  5.   

    对(沧海一声笑)说的“你发送数据后,PER_IO_OPERATION_DATA 的操作改为了什么 ?如果没改的话,那它当然在循环了。”不太明白:(
    请问到底是应该改还是不应该改?
      

  6.   

    关键代码没有贴出来,不是太清楚,另外楼主是否确定自己在服务器端收到的就是客户端发来的内容,如果不是,那么可能是你的IOCP没有与相关的Socket进行关联.
      

  7.   

    if (bool bOK = GetQueuedCompletionStatus(m_CompletionPort, &BytesTransferred, (LPDWORD)&m_pPerHandleData, (LPOVERLAPPED *)&m_pPerIoData, INFINITE) == 0)
    {
    continue;
    }答复楼上说的,肯定关联了,否则在调用GetQueuedCompletionStatus的地方肯定都不会返回了
      

  8.   

    void CWorkerThread::SendPacket(char* pBufHeader, char* pBufData, int nDataLen)
    {
    //PER_IO_OPERATION_DATA * PerIoData = new PER_IO_OPERATION_DATA;
    int iRtn = 0;
    DWORD dwSendBytes, dwFlags;//, dwRecvBytes;
    char pBuffer[1024] = {0};

    memcpy(pBuffer, pBufHeader, MESSAGE_HEADER_LEN);
    memcpy(pBuffer + MESSAGE_HEADER_LEN, pBufData, nDataLen);
    ZeroMemory(&(m_pPerIoData->Overlapped), sizeof(OVERLAPPED)); 
    m_pPerIoData->DataBuf.buf = pBuffer;
    m_pPerIoData->DataBuf.len = MESSAGE_HEADER_LEN + nDataLen; iRtn = WSASend(m_pPerHandleData->m_hSocket, &(m_pPerIoData->DataBuf), 1, &dwSendBytes, 0, &(m_pPerIoData->Overlapped), NULL);
    if (iRtn == SOCKET_ERROR) 
    {
    if (WSAGetLastError() != ERROR_IO_PENDING)
    {
    TRACE("WSASend() failed with error %d\n", WSAGetLastError());
    }
    }
    }
      

  9.   

    你send后GetQueuedCompletionStatus 会得到send后操作的数据字节
    你的操作是send所有的数据
    确认send所有的数据后,再保持到read状态.你说发生的现象在我看了是正常的.
      

  10.   

    也就是说 基于该socket的收、发、断开等事件都可以被完成端口感知
      

  11.   

    天使亲蛙能否告诉偶在send之后应该怎么做?怎么样才能“再保持到read状态.”?
      

  12.   

    int CWorkerThread::Run() 
    {
    DWORD BytesTransferred;
    DWORD dwFlags;
    int iRtn = 0;
    char s[1024] = {0}; while(TRUE) 
    {
    if (bool bOK = GetQueuedCompletionStatus(m_CompletionPort, &BytesTransferred, (LPDWORD)&m_pPerHandleData, (LPOVERLAPPED *)&m_pPerIoData, INFINITE) == 0)
    {
    TRACE("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
    //PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    continue;
    }
    else
    {
    TRACE("sizeof(PER_HANDLE_DATA)=%d, sizeof(PER_IO_OPERATION_DATA)=%d\n", sizeof(PER_HANDLE_DATA), sizeof(PER_IO_OPERATION_DATA));

    if (BytesTransferred == 0)
    {
    TRACE("Closing socket %d\n", m_pPerHandleData->m_hSocket); if (closesocket(m_pPerHandleData->m_hSocket) == SOCKET_ERROR)
    {
    printf("closesocket() failed with error %d\n", WSAGetLastError());
    continue;
    }
    m_pServerThread->RemoveOnlineUser(m_pPerHandleData);
    GlobalFree(m_pPerHandleData);
    GlobalFree(m_pPerIoData);
    continue;
    }
    else
    {
    bool bExists = m_mapOnLineSocket.Lookup(m_pPerHandleData->m_hSocket, m_pPerHandleData);
    if(bExists == false)
    {
    //m_pPerHandleData = PerHandleData;
    //m_mapOnLineSocket.SetAt(PerHandleData->m_hSocket, m_pPerHandleData);
    }
    ASSERT(m_pPerHandleData != NULL);
    if (m_pPerHandleData->m_bHeadRecv == false)
    {
    if(m_pPerHandleData->m_u16DataRead + BytesTransferred < MESSAGE_HEADER_LEN)
    {
    m_pPerHandleData->m_u16DataRead += BytesTransferred;
    PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    continue;
    }
    else if(m_pPerHandleData->m_u16DataRead + BytesTransferred == MESSAGE_HEADER_LEN)
    {
    memcpy(&m_pPerHandleData->m_pBuffer[m_pPerHandleData->m_u16DataRead], m_pPerIoData->Buffer, MESSAGE_HEADER_LEN - m_pPerHandleData->m_u16DataRead);
    if(!AnalysisHeader())
    {
    ClearSocket(m_pPerHandleData);
    continue;
    }
    else
    {
    m_pPerHandleData->m_u16DataRead = 0;
    m_pPerHandleData->m_bHeadRecv = TRUE;
    PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    continue;
    }
    }
    else
    {
    memcpy(&m_pPerHandleData->m_pBuffer[m_pPerHandleData->m_u16DataRead], m_pPerIoData->Buffer, MESSAGE_HEADER_LEN - m_pPerHandleData->m_u16DataRead);
    if(!AnalysisHeader())
    {
    ClearSocket(m_pPerHandleData);
    continue;
    }
    else
    {
    m_pPerHandleData->m_u16DataRead = BytesTransferred - MESSAGE_HEADER_LEN;
    m_pPerHandleData->m_bHeadRecv = TRUE;
    }
    }

    if(m_pPerHandleData->m_u16DataRead < m_pPerHandleData->m_u16DataNum)
    {
    //PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    continue;
    }
    else if(m_pPerHandleData->m_u16DataRead > m_pPerHandleData->m_u16DataNum)
    {
    //ASSERT(0);
    }
    else
    {
    memcpy(m_pPerHandleData->m_pBuffer, m_pPerIoData->Buffer + MESSAGE_HEADER_LEN, m_pPerHandleData->m_u16DataNum);
    //处理收到的数据结构
    ProcessPacketData();
    m_pPerHandleData->m_u16DataRead = 0;
    m_pPerHandleData->m_bHeadRecv = false;
    PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    }
    }
    else
    {

    if(m_pPerHandleData->m_u16DataRead + BytesTransferred < m_pPerHandleData->m_u16DataNum)
    {
    m_pPerHandleData->m_u16DataRead += BytesTransferred;
    //PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    continue;
    }
    else if(m_pPerHandleData->m_u16DataRead + BytesTransferred > m_pPerHandleData->m_u16DataNum)
    {
    //ASSERT(0);
    }
    else
    {
    memcpy(&m_pPerHandleData->m_pBuffer[m_pPerHandleData->m_u16DataRead], m_pPerIoData->Buffer, BytesTransferred);
    //处理收到的数据结构
    ProcessPacketData();
    m_pPerHandleData->m_u16DataRead = 0;
    m_pPerHandleData->m_bHeadRecv = false;
    PrepareForRecv(m_pPerHandleData, m_pPerIoData);
    }
    }
    }
    }
    }

    return 0;
    }
      

  13.   

    send操作后 GetQueuedCompletionStatus 的 lpNumberOfBytes 是你send的长度,你就可以read了啊..
      

  14.   

    代码里面的PrepareForRecv(m_pPerHandleData, m_pPerIoData);如下:
    void CWorkerThread::PrepareForRecv(PER_HANDLE_DATA * PerHandleData, PER_IO_OPERATION_DATA * PerIoData)
    {
    DWORD dwFlags = 0;
    int iRtn = 0;
    DWORD dwRecvBytes = 0; ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.len = DATA_BUFSIZE;
    PerIoData->DataBuf.buf = PerIoData->Buffer;
    iRtn = WSARecv(PerHandleData->m_hSocket, &(PerIoData->DataBuf), 1, &dwRecvBytes, &dwFlags, &(PerIoData->Overlapped), NULL);
    if (iRtn == SOCKET_ERROR)
    {
    if (WSAGetLastError() != ERROR_IO_PENDING)
    {
    TRACE("WSARecv() failed with error %d\n", WSAGetLastError());
    //return 0;
    }
    }
    }是否每次发送或者接收之后,都要发送一个wsarecv请求?原来没有添加这个操作的时候,线程就挂在调用get的地方了。我的workerthread的任务是接收数据(可能一次接收不到所需要的数据,这时将继续调用get函数,这时候如果不执行一个wsarecv调用,下一次的get就不会返回了),直到收到足够的数据,就调用处理函数,并通过wsasend发送响应数据。到现在我还没搞明白调用wsasend(比如说我发送了400字节的数据)之后,get返回,接收到的数据长度是刚才发送的数据长度,这个到底是怎么回事。按上面各位说的,这是iocp返回的send通知。哪位能告诉我应该怎样修改我的接收线程呢?
      

  15.   

    老大,这时我读到的数据结构中什么都没有就是保存Read状态 等待接受数据诶..wsasend的调用导致了get返回,我怎么知道这是个send的通知呢?如果没办法区分的话,...."我怎么知道这是个send的通知呢?" 你说对地方了,这个就是我们需要做的地方.
    send 还是 read 要在lpCompletionKey 结构中标记状态,这样GetQueuedCompletionStatus后不就知道了..这个状态怎么设置呢?
    是在调用wsasend或者wsarecv之前设置吗?
      

  16.   

    给一个windows网络编程第二版中的例子:DWORD WINAPI ServerWorkerThread(
        LPVOID CompletionPortID)
    {
        HANDLE CompletionPort = (HANDLE) CompletionPortID;
        DWORD BytesTransferred;
        LPOVERLAPPED Overlapped;
        LPPER_HANDLE_DATA PerHandleData;
        LPPER_IO_DATA PerIoData;
        DWORD SendBytes, RecvBytes;
        DWORD Flags;
        
        while(TRUE)
        {
            // Wait for I/O to complete on any socket
            // associated with the completion port
        
            ret = GetQueuedCompletionStatus(CompletionPort,
                &BytesTransferred,(LPDWORD)&PerHandleData,
                (LPOVERLAPPED *) &PerIoData, INFINITE);        // First check to see if an error has occurred
            // on the socket; if so, close the 
            // socket and clean up the per-handle data
            // and per-I/O operation data associated with
            // the socket        if (BytesTransferred == 0 &&
                (PerIoData->OperationType == RECV_POSTED ││
                 PerIoData->OperationType == SEND_POSTED))
            {
                // A zero BytesTransferred indicates that the
                // socket has been closed by the peer, so
                // you should close the socket. Note: 
                // Per-handle data was used to reference the
                // socket associated with the I/O operation.
     
                closesocket(PerHandleData->Socket);            GlobalFree(PerHandleData);
                GlobalFree(PerIoData);
                continue;
            }        // Service the completed I/O request. You can
            // determine which I/O request has just
            // completed by looking at the OperationType
            // field contained in the per-I/O operation data.
             if (PerIoData->OperationType == RECV_POSTED)
            {
                // Do something with the received data
                // in PerIoData->Buffer
            }        // Post another WSASend or WSARecv operation.
            // As an example, we will post another WSARecv()
            // I/O operation.        Flags = 0;        // Set up the per-I/O operation data for the next
            // overlapped call
            ZeroMemory(&(PerIoData->Overlapped),
                sizeof(OVERLAPPED));        PerIoData->DataBuf.len = DATA_BUFSIZE;
            PerIoData->DataBuf.buf = PerIoData->Buffer;
            PerIoData->OperationType = RECV_POSTED;        WSARecv(PerHandleData->Socket, 
                &(PerIoData->DataBuf), 1, &RecvBytes,
                &Flags, &(PerIoData->Overlapped), NULL);
        }
    }
      

  17.   

    再问一句,传给get的key和ol的指针都不用初始化,
    而是在post 读写操作的时候初始化,对吗?
      

  18.   

    传给get的key和ol的指针在Get..函数中设置值的,应该可以不用初始化