WinSock2 IOCP WSARecv GetQueuedCompletionStatus:数据(自动)最终在char *缓冲区中,而不是WSABUF.buf ...为什么?

时间:2012-10-03 15:03:48

标签: c++ c winsock2 iocp

在调试时,当调用WSARecv时,我为该函数提供了 PerIoData-> WSABUF 结构的地址。这应该将发送的数据分配给 WSABUF.buf char *数组,它似乎是这样。当工作线程循环回等待的 GetQueuedCompletionStatus 时,它似乎以某种方式(神奇地)将该数据发送到PerIoData.Buffer(char * array)。基本上, PerIoData.Buffer PerIoData.WSABUF.buf 都等于相同的char *数组。当我从 PER_IO_DATA Struct中删除 PerIoData.Buffer 时(以及对它的所有引用),当客户端发送数据时, GetQueuedCompletionStaus 永远不会返回我知道 WSABUF.buf 应填充数据。

相关信息:

  1. 我正在实施“Microsoft Windows网络编程”中的完成端口模型(第157页)。虽然那本书中的例子很难被独立发现,但我的代码现在工作正常。
  2. 在ServerWorkerThread的while循环中:GetQueuedCompletionStatus,首先调用,接收per_handle_data和per_io_data
  3. per_io_data struct就是这样:

    struct _PER_IO_DATA{ //in the interests of an efficient question, i'm omitting the
    //constructor/destructor code
    public:
        OVERLAPPED Overlapped;
        WSABUF DataBuf;
        char myBuffer[BUFFER_LENGTH];
        int BufferLen;
        int OperationType;
    };
    typedef _PER_IO_DATA PER_IO_DATA;
    typedef _PER_IO_DATA *PPER_IO_DATA;
    
  4. 我的GetQueuedCompletionStatus函数被调用如下:

    ret = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData, INFINITE);

  5. 我的WSARecv函数被调用如下:

    WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, NULL, &Flags, ((LPWSAOVERLAPPED)&PerIoData->Overlapped), NULL); //i know casting the Overlapped structure as LPWSAOVERLAPPED is unnecessary, but I was tweaking the //code when I didn't fully understand the problems I was having.

  6. 我的问题是我从未明确地将任何内容分配给 PerIoData-&gt;缓冲区,但似乎总是会填充已发送的数据。我相信GetQueuedCompletionStatus“知道”将此数据发送到 PerIoData-&gt;缓冲区,尽管它期望指向 LPOVERLAPPED 结构(我通过我的指针) PerIoData 包含所讨论的缓冲区 char数组的struct实例。它真的让我烦恼......也许它的行为并不像我想的那样,但我唯一可以看到 PerIoData-&gt; Buffer 的地方来自 GetQueuedCompletionStatus < / strong>方法。如果情况并非如此,那么 PerIoData-&gt;缓冲区似乎无处不在?我已经搜索了MSDN和google好几天了。我会继续寻找,如果我找到答案,我会发布更新。请帮忙?提前谢谢!

    *注意:我会创建标签WSABUF和GetQueuedCompletionStatus,但这是我的第一篇文章。

    - 编辑:我发布了结构和工作线程,遗漏了所有其他不相关的代码.-- 您会注意到_PER_IO_DATA :: DataBuf.buf已分配内存,然后清零。没有指向myBuffer数组......

    #include "stdafx.h"
    #define SEND_POSTED 1
    #define RECV_POSTED 2
    #define BUFFER_LENGTH 1024
    
    HANDLE CompletionPort;
    SOCKADDR_IN serverAddress, *clientAddress;
    SOCKET listener, client;
    unsigned short port = 5000;
    SYSTEM_INFO SystemInfo;
    int i;
    
    struct _PER_HANDLE_DATA{//Per handle data structure
        SOCKET Socket;
        SOCKADDR_STORAGE Address;
        _PER_HANDLE_DATA(){
            Socket = 0;
            ZeroMemory(&Address, sizeof(SOCKADDR_STORAGE));
        }
        ~_PER_HANDLE_DATA(){
            Socket = NULL;
            ZeroMemory(&Address, sizeof(SOCKADDR_STORAGE));
        }
    };typedef _PER_HANDLE_DATA PER_HANDLE_DATA;typedef _PER_HANDLE_DATA *PPER_HANDLE_DATA;
    
    
    struct _PER_IO_DATA{
    public:
        OVERLAPPED Overlapped;
        WSABUF DataBuf;
        char myBuffer[BUFFER_LENGTH];
        int BufferLen;
        int OperationType;
        _PER_IO_DATA(){
            OperationType = 0;
            DataBuf.len = BUFFER_LENGTH;
            DataBuf.buf = (char*)malloc(BUFFER_LENGTH+1);
            BufferLen = BUFFER_LENGTH;
            ZeroMemory(DataBuf.buf, (sizeof(BUFFER_LENGTH+1)));
            ZeroMemory(&myBuffer, (sizeof(char)*BUFFER_LENGTH));
            SecureZeroMemory((PVOID)&Overlapped, sizeof(Overlapped));
        }
        ~_PER_IO_DATA(){
            free(&DataBuf.buf);
        }
    };
    typedef _PER_IO_DATA PER_IO_DATA;
    typedef _PER_IO_DATA *PPER_IO_DATA;
    
    
    unsigned _stdcall ServerWorkerThread(LPVOID CompletionPortID);
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        /*
            INITIALIZE WINSOCK AND COMPLETION PORT, AND ACCEPT CONNECTIONS
        */
    }
    
    unsigned _stdcall ServerWorkerThread(LPVOID CompletionPortID){
        printf("ServerWorkerThread(%d) Working\n", GetCurrentThreadId());
        HANDLE CompletionPort = (HANDLE) CompletionPortID;
        DWORD BytesTransferred;
        PPER_HANDLE_DATA PerHandleData = new PER_HANDLE_DATA;
        PPER_IO_DATA PerIoData = new PER_IO_DATA;
        DWORD SendBytes = 0, RecvBytes = 0;
        DWORD Flags;
        BOOL ret;
        Sleep(2000);
        while(TRUE){
            ret = GetQueuedCompletionStatus(CompletionPort,
                &BytesTransferred,
                (LPDWORD)&PerHandleData,
                (LPOVERLAPPED *)&PerIoData,
                INFINITE);
            //printf("\n\nBytesTransferred: %d\n\n", BytesTransferred);
            if(BytesTransferred == 0 && (PerIoData->OperationType == RECV_POSTED || PerIoData->OperationType == SEND_POSTED)){
                closesocket(PerHandleData->Socket);
                GlobalFree(PerHandleData);
                GlobalFree(PerIoData);
                continue;
            }
            if(PerIoData->OperationType == RECV_POSTED){
            //output received data
                if(!strcmp(PerIoData->DataBuf.buf, "Disconnect") || !strcmp(PerIoData->DataBuf.buf, "disconnect")){
                    printf("Disconnecting...\n");
                    if(!shutdown(PerHandleData->Socket, SD_BOTH)){
                        closesocket(PerHandleData->Socket);
                        delete(PerHandleData);
                    }
                }else{
                    printf("RECV_POSTED: %s\n", PerIoData->DataBuf.buf);
                }
            }
            Flags = 0;
    
            SecureZeroMemory((PVOID)&PerIoData->Overlapped, sizeof(WSAOVERLAPPED));
            PerIoData->DataBuf.len = BUFFER_LENGTH;
            //***************************************************************************
            //Even though the following is commented out, PerIoData->DataBuf.buf 
            //is still being populated and so is PerIoData-myBuffer
            //So why is myBuffer being populated with data when DataBuf.buf is not pointing to it??
            //PerIoData->DataBuf.buf = PerIoData->myBuffer;
            //Also, if you comment out all references of myBuffer, GetQueuedCompletionStatus(),
            //will never return if myBuffer doesn't exist...how does it seem to be 'aware' of myBuffer?
            //***************************************************************************
            PerIoData->OperationType = RECV_POSTED;
            WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, NULL, &Flags, ((LPWSAOVERLAPPED)&PerIoData->Overlapped), NULL);
        }
        return 0;
    }
    

0 个答案:

没有答案