Original Post
I'm trying to get my head around IO-completion ports and out-of-order packets (due to scheduling of the threads). My current implementation is working (blissfully ignoring out-of-order packets or even fragmented packets). The key part is the thread::run-function in the ::OPCODE_RECV_COMPLETED case-part, where I just call a client-callback passing the bytestream which was received. Now I'd like to make sure the packet-order is correct - I looked for some code/examples (found some on codeproject), but they are either cryptic/hard to follow or without any documentation. Assume I post two writes, I have two iocp-threads and both get one of the writes. Now it depends on the OS which one goes first (same for read) So what now? Add a mutex/critical-section? - Wouldn't that beat the purpose of having several iocp-threads? - And what about the network-card? I image at some point the driver will write something into it's buffers - at least there should be some kind of lock because 2 threads writing are probably bad? Can anyone explain some background on how it's supposed to work (or maybe someone knows a recent book discussing this problem?) My iocp-thread::run function looks like this
bool TCPIocpThread::run (float _dtInSeconds)
{
TCPSocketIocp* iocp_socket;
OVERLAPPED* overlapped_intern;
Context* overlapped_ext;
DWORD dwIoSize;
// 1st parameter is the value we passed when creating the completion port
// so we can cast the dword pointer to whatever type we used on creation
bool iocpStatus = iocp->getStatus((PDWORD_PTR)&iocp_socket, &dwIoSize, &overlapped_intern);
if (!iocpStatus)
return true;
// A completion key of 0 is posted to the iocp-thread to request shutdown
if (!iocp_socket)
return false;
// cast it to our extended overlapped structure
overlapped_ext = (Context*) overlapped_intern;
switch (overlapped_ext->opcode)
{
case Context::OPCODE_SEND_REQUEST:
{
// start transmitting, reuse original overlapped structure
DWORD dwNumBytesSent = 0, ret;
// when iocp returns, status is 'send_completed'
overlapped_ext->opcode = Context::OPCODE_SEND_COMPLETED;
ret = WSASend( iocp_socket->socket, &overlapped_ext->wsabuf, 1, &dwNumBytesSent, 0, overlapped_ext, NULL );
DWORD dwLastError = WSAGetLastError();
if ( ret == 0 )
return true;
else if (ret == SOCKET_ERROR && dwLastError == WSA_IO_PENDING )
return true;
else
{
iocp_socket->onConnectionError (dwLastError, "TCPIocpThread::run(OPCODE_SEND_REQUEST)");
iocp_socket->freeContext (overlapped_ext);
iocp_socket->closeSocket(false);
}
}
break;
case Context::OPCODE_RECV_HDR_REQUEST:
case Context::OPCODE_RECV_REQUEST:
{
// when iocp returns, status will be 'OPCODE_RECV_(HDR_)COMPLETED'
if (overlapped_ext->opcode==Context::OPCODE_RECV_HDR_REQUEST)
overlapped_ext->opcode = Context::OPCODE_RECV_HDR_COMPLETED;
else
overlapped_ext->opcode = Context::OPCODE_RECV_COMPLETED;
DWORD dwNumBytes = 0, dwFlags = 0;
if (WSARecv( iocp_socket->socket, &overlapped_ext->wsabuf, 1, &dwNumBytes, &dwFlags, overlapped_ext, NULL)==SOCKET_ERROR)
{
DWORD dwLastError = ::WSAGetLastError();
if (ERROR_IO_PENDING != dwLastError)
iocp_socket->onConnectionError (dwLastError, "TCPIocpThread::run(OPCODE_RECV_REQUEST)");
else
{
//iocp_socket->onConnectionError (dwLastError, "TCPIocpThread::run(OPCODE_RECV_REQUEST)");
//iocp_socket->freeContext (overlapped_ext);
//iocp_socket->disconnectSocket(false);
}
}
}
break;
case Context::OPCODE_RECV_HDR_COMPLETED:
{
iNetCallback* handler = iocp_socket->pHandler;
// we received the header - interpret it to determine how much data is following
U32 datalen = handler->getProtocol()->getDataLength ((const U8*)overlapped_ext->wsabuf.buf, overlapped_ext->wsabuf.len);
NetId netSrc = handler->getProtocol()->getNetDest((const U8*)overlapped_ext->wsabuf.buf, overlapped_ext->wsabuf.len);
iocp_socket->freeContext (overlapped_ext);
// post read-request for the actual data
iocp_socket->setInitialReadRequest (datalen, netSrc);
}
break;
case Context::OPCODE_RECV_COMPLETED:
{
iNetCallback* handler = iocp_socket->pHandler;
// we received the data which followed the header
<b>handler->onReceivedData (0,overlapped_ext->netSrc, overlapped_ext->wsabuf.buf, overlapped_ext->wsabuf.len);</b>
iocp_socket->freeContext (overlapped_ext);
// post next read request (read next header)
iocp_socket->setInitialReadRequest();
}
break;
case Context::OPCODE_SEND_COMPLETED:
{
iocp_socket->freeContext (overlapped_ext);
}
break;
case Context::OPCODE_DISCONNECT:
{
//::shutdown (iocp_socket->socket, SD_BOTH);
//closesocket( iocp_socket->socket);
//iocp_socket->freeContext (overlapped_ext);
}
break;
/*case OPCODE_CONNECT:
{
}
break;*/
default:
{
}
break;
}
return true;
}