Search code examples
csocketswinapiwinsockiocp

What is the order of the completion packets for WSASend() and WSARecv() in this case?


Say that I have two programs, a client and a server, and that I am running the client and the server on the same computer (so the speed is extremely fast), and say that the client socket's receive buffer is empty, and that the server will not send any data to the client except if the client told the server to do so.

Now in the client, I call WSASend() and then after it I call WSARecv():

WSASend(...);    // tell the server to send me some data
WSARecv(...);

So in the code above, WSASend() is telling the server to send some data to the client (for example: the string "hello").

Now after some time, two completion packets will be placed in the completion port:

  • The first completion packet is for WSASend() (telling me that the data has been placed in the client socket's send buffer).

  • The second completion packet is for WSARecv() (telling me that the data has been placed in the buffer that I passed to WSARecv() when I called it).

Now my question is: is it possible that the completion packet for WSARecv() be placed in the completion port before the completion packet for WSASend() (so when I call GetQueuedCompletionStatus() I will get the completion packet for WSARecv() first)?


Solution

  • you must never assume any order of completion packets you got. you must have independent from this knowledge - which is operation complete.

    you must define some structure inherited from OVERLAPPED and it this structure place all data related to operation. including tag which describe type of operation. so when you extract pointer to OVERLAPPED from IOCP you cast it to this structure and will be know - are this for recv or send, connect or disconnect.. for example

    class IO_IRP : public OVERLAPPED 
    {
      //...
      DWORD m_opCode;// `recv`, `send`, `dsct`, `cnct`
    
      IO_IRP(DWORD opCode,...) : m_opCode(opCode) {}
    
        VOID IOCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered)
        {
            // switch (m_opCode)
            m_pObj->IOCompletionRoutine(m_packet, m_opCode, dwErrorCode, dwNumberOfBytesTransfered, Pointer);
            delete this;
        }
    
        static VOID CALLBACK _IOCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
        {
            static_cast<IO_IRP*>(lpOverlapped)->IOCompletionRoutine(dwErrorCode, dwNumberOfBytesTransfered);
        }
    
    };
    
    // recv
    if (IO_IRP* Irp = new IO_IRP('recv', ..))
    {
      WSARecv(..., Irp);
      ...
    }