1

While debugging, when WSARecv is called, I supply the function with the address of the PerIoData->WSABUF structure. This should assign the sent data to the WSABUF.buf char* array, which it seems to. When The worker thread loops back to the waiting GetQueuedCompletionStatus, it seems to somehow (magically) send that data to PerIoData.Buffer (char* array). So essentially, the PerIoData.Buffer and PerIoData.WSABUF.buf both equal the same char* array. When I remove the PerIoData.Buffer from the PER_IO_DATA Struct (and all references to it), the GetQueuedCompletionStaus never returns when the client sends data though i know the WSABUF.buf should be populated with data.

The pertinent information:

  1. I'm implementing the Completion Port Model found in "Network Programming for Microsoft Windows" (p.157). Though the examples in that book left much to be independently discovered, my code works fine now.
  2. In the while loop of the ServerWorkerThread: GetQueuedCompletionStatus , called first, receives per_handle_data, and per_io_data
  3. per_io_data struct is as such:

    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. My GetQueuedCompletionStatus function is called like so:

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

  5. My WSARecv Function is called like so:

    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.

My problem is that I never explicitly assign anything to the PerIoData->Buffer yet it seems to always get populated with the sent data. I'm lead to believe GetQueuedCompletionStatus "knows" to send this data to that PerIoData->Buffer though it's expecting a pointer to a LPOVERLAPPED structure (to which i pass my PerIoData struct instance containing the Buffer char array in question). It's really bugging me... Maybe it's not behaving like I'm thinking it is, but the only place I can see the PerIoData->Buffer being populated is from within the GetQueuedCompletionStatus method. If that's not the case, then PerIoData->Buffer seems to be populated from nowhere? I've scoured MSDN and google for days. I'll continue looking and if I find the answer I'll post an update. Please Help? Thanks in advance!

*Note: I would've created the tags WSABUF and GetQueuedCompletionStatus, but this is my first post.

--EDIT: I'm posting the structs and worker thread, leaving out all other unrelated code.-- You'll notice that _PER_IO_DATA::DataBuf.buf is allocated memory, then zeroed out. Not pointing to the myBuffer array....

#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;
}
Manjunath Ballur
  • 6,287
  • 3
  • 37
  • 48
hwill
  • 29
  • 7
  • are you saying you're intentionally calling WSARecv() with a WSABUF having both the data and length members undefined, yet by seemingly magic WSARecv populates your buffer anyway? (I *think* thats what I read) – WhozCraig Oct 03 '12 at 15:12
  • Not Exactly, I can't assign anything to the an uninitialized char* array so at the very least WSABUF.len needs to have some value. From there WSARecv Sends the data to WSABUF.buf But yes, I can't determine where my custom char array (called Buffer) gets its value from or why GetQueuedCompletionStatus doesn't return unless that custom char array Buffer is there. – hwill Oct 03 '12 at 15:52
  • There is no "magic" to it. You have a single buffer in memory (the `PER_IO_DATA::myBuffer` field). The `WSABUF` struct tells `WSARecv()` where that buffer is and how large it is (`WSARecv()` uses `WSABUF` to describe buffers because it supports writing to multiple buffers at a time). When the buffer has been filled with some data, `GetQueuedCompletionStatus()` reports its status, returning the same `OVERLAPPED` pointer that you passed to `WSARecv()` so you can know which operation was completed. What is confusing about that? – Remy Lebeau Oct 03 '12 at 19:55
  • I see what's going on.. Remy, you've clarified how this program _should_ behave, but my app doesn't run like what you're describing. I allocate, then zero the memory of the WSABUF.buf when the PER_IO_DATA* struct is initialized. So WSABUF (in my program) _shouldn't_ know where PER_IO_DATA::myBuffer is located (I never, at any point, assign the address of myBuffer to WSABUF::buf). But it still receives the data. My confusion: how does WSABUF tell WSARecv() where myBuffer is if I don't ever assign the address of myBuffer to WSABUF.buf? – hwill Oct 03 '12 at 22:00
  • And.. rewind back up to the first comment. Thats what I *thought* you were saying. And if that is the case I am just as stymied as you. WSABUF is how you set your buffer location awareness for a WSARecv() invoke, or so I have always done. Now I wanna slam out a simple client-server pair and see this shiznit for myself. – WhozCraig Oct 03 '12 at 22:38
  • A long ago i wrote an example of server using IOCP. Take a look: http://intara.arrowdodger.ru/h/iocp.c – arrowd Oct 04 '12 at 13:16

0 Answers0