Reputation: 69
I'm trying to learn IOCP with C in Windows. The idea is to have a non-blocking server with threads that handle client messages.
I understand by now how to accept clients, etc.
But I still don't understand how to read each client's messages from GetQueuedCompletionStatus()
and print them.
I see online that all people pass a struct to reference it with GetQueuedCompletionStatus()
using the LPOVERLAPPED
parameter, but how to do it without any struct?
Can someone tell me if that is possible, and show a little example?
Upvotes: 0
Views: 139
Reputation: 597101
You have to keep track of the buffers you pass to WSARecv()
, etc. You do that by associating an OVERLAPPED
with each buffer. When GetQueuedCompletionStatus()
tells you which OVERLAPPED
is finished, you thus known the associated buffer is ready.
This is commonly handled by defining a struct
with an OVERLAPPED
as its first member, so that a pointer to the OVERLAPPED
is also equal to a pointer to the struct
. Then you can put whatever other details you want in the struct
, like the buffer, socket, etc, whatever your app needs to process the data.
For example:
enum IocpOp { opRead, opWrite };
typedef struct
{
WSAOVERLAPPED ov;
IocpOp opCode;
SOCKET sckt;
BYTE buffer[256];
DWORD numBytesInBuffer;
} MYIOCPINFO, *PMYIOCPINFO;
void IoComplete(PMYIOCPINFO io, DWORD errorCode, DWORD numBytesTransferred)
{
if (errorCode != ERROR_SUCCESS)
{
// handle failure as needed ...
free(io);
return;
}
switch (io->opCode)
{
case opRead:
// use io->buffer up to numBytesTransferred as needed ...
break;
case opWrite:
if (numBytesTransferred < io->numBytesInBuffer)
{
info->numBytesInBuffer -= numBytesTransferred;
memmove(io->buffer, io->buffer + numBytesTransferred, io->numBytesInBuffer);
WSABUF buf;
buf.len = io->numBytesInBuffer;
buf.buf = (CHAR*) io->buffer;
if (WSASend(io->sckt, &buf, 1, NULL, 0, &(io->ov), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
// handle failure as needed ...
free(io);
}
}
return;
}
break;
}
// reuse io for the next I/O operation on io->sckt
// or free io now if done using it ...
free(io);
}
}
...
PMYIOCPINFO io = malloc(sizeof(MYIOCPINFO));
if (!io) ...
io->opCode = opRead;
io->sckt = sckt;
WSABUF buf;
buf.len = sizeof(io->buffer);
buf.buf = (CHAR*) io->buffer;
DWORD dwFlags = 0;
if (WSARecv(sckt, &buf, 1, NULL, &dwFlags, &(io->ov), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
IoComplete(io, WSAGetLastError(), 0);
}
...
PMYIOCPINFO io = malloc(sizeof(MYIOCPINFO));
if (!io) ...
io->opCode = opWrite;
io->sckt = sckt;
// fill io->buffer as needed ...
io->numBytesInBuffer = ...;
WSABUF buf;
buf.len = io->numBytesInBuffer;
buf.buf = (CHAR*) io->buffer;
if (WSASend(sckt, &buf, 1, NULL, 0, &(io->ov), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
IoComplete(io, WSAGetLastError(), 0);
}
...
DWORD dwBytesTransferred = 0;
ULONG_PTR ulCompletionKey = 0;
LPOVERLAPPED lpOverlapped = NULL;
if (GetQueuedCompletionStatus(hIOCP, &dwBytesTransferred, &ulCompletionKey, &lpOverlapped, INFINITE))
{
if (ulCompletionKey == MySocketIOCPKey) // specified in CreateIOCompletionPort()...
{
IoComplete((PMYIOCPINFO)lpOverlapped, ERROR_SUCCESS, dwBytesTransferred);
}
else
{
...
}
}
else
{
if (lpOverlapped)
{
// I/O operation belonging to ulCompletionKey failed...
if (ulCompletionKey == MySocketIOCPKey) // specified in CreateIOCompletionPort()...
{
IoComplete((PMYIOCPINFO)lpOverlapped, GetLastError(), dwBytesTransferred);
}
else
{
...
}
}
else
{
// GetQueuedCompletionStatus() itself failed...
}
}
Upvotes: 2