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:
- 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.
- In the while loop of the ServerWorkerThread: GetQueuedCompletionStatus , called first, receives per_handle_data, and per_io_data
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;
My GetQueuedCompletionStatus function is called like so:
ret = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *)&PerIoData, INFINITE);
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;
}