< WSAEventSelect Program Example | Winsock2 I/O Method Main | Overlapped & AcceptEx() Example >


 

 

Winsock 2 I/O Methods 5 Part 6

 

 

What do we have in this chapter 5 part 6?

  1. The Overlapped Model

  2. Event Notification

  3. The Overlapped Model Program Example

  4. Running the Server and Client Program

 

The Overlapped Model

 

The overlapped I/O model in Winsock offers applications better system performance than any of the I/O models explained so far. The overlapped model's basic design allows your application to post one or more asynchronous I/O requests at a time using an overlapped data structure. At a later point, the application can service the submitted requests after they have completed. This model is available on all Windows platforms except Windows CE. The model's overall design is based on the Windows overlapped I/O mechanisms available for performing I/O operations on devices using the ReadFile() and WriteFile() functions.

Originally, the Winsock overlapped I/O model was available only to Winsock 1.1 applications running on Windows NT. Applications could take advantage of the model by calling ReadFile() and WriteFile() on a socket handle and specifying an overlapped structure. Since the release of Winsock 2, overlapped I/O has been incorporated into new Winsock functions, such as WSASend() and WSARecv(). As a result, the overlapped I/O model is now available on all Windows platforms that feature Winsock 2.

With the release of Winsock 2, overlapped I/O can still be used with the functions ReadFile() and WriteFile() under Windows NT and Windows 2000. However, this functionality was not available for Windows 95, Windows 98, and Windows Me. For compatibility across platforms, you should always consider using the WSARecv() and WSASend() functions instead of the Windows ReadFile() and WriteFile() functions. This section will only describe how to use overlapped I/O through the Winsock 2 functions.

To use the overlapped I/O model on a socket, you must first create a socket that has the overlapped flag set. After you successfully create a socket and bind it to a local interface, overlapped I/O operations can commence by calling the Winsock functions listed below and specifying an optional WSAOVERLAPPED structure.

 

  1. WSASend()
  2. WSASendTo()
  3. WSARecv()
  4. WSARecvFrom()
  5. WSAIoctl()
  6. WSARecvMsg()
  7. AcceptEx()
  8. ConnectEx()
  9. TransmitFile()
  10. TransmitPackets()
  11. DisconnectEx()
  12. WSANSPIoctl()

 

To use overlapped I/O, each function takes a WSAOVERLAPPED structure as a parameter. When these functions are called with a WSAOVERLAPPED structure, they complete immediately, regardless of the socket's mode (described at the beginning of this chapter). They rely on the WSAOVERLAPPED structure to manage the completion of an I/O request. There are essentially two methods for managing the completion of an overlapped I/O request: your application can wait for event object notification or it can process completed requests through completion routines. The first six functions in the list have another parameter in common: a WSAOVERLAPPED_COMPLETION_ROUTINE. This parameter is an optional pointer to a completion routine function that gets called when an overlapped request completes. We will explore the event notification method next. Later in this chapter, you will learn how to use optional completion routines instead of events to process completed overlapped requests.

 

Event Notification

 

The event notification method of overlapped I/O requires associating Windows event objects with WSAOVERLAPPED structures. When I/O calls such as WSASend() and WSARecv() are made using a WSAOVERLAPPED structure, they return immediately. Typically, you will find that these I/O calls fail with the return value SOCKET_ERROR and that WSAGetLastError() reports a WSA_IO_PENDING error status. This error status simply means that the I/O operation is in progress. At a later time, your application will need to determine when an overlapped I/O request completes by waiting on the event object associated with the WSAOVERLAPPED structure. The WSAOVERLAPPED structure provides the communication medium between the initiation of an overlapped I/O request and its subsequent completion, and is defined as:

 

typedef struct WSAOVERLAPPED

{

    DWORD    Internal;

    DWORD    InternalHigh;

    DWORD    Offset;

    DWORD    OffsetHigh;

    WSAEVENT hEvent;

} WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;

 

The Internal, InternalHigh, Offset, and OffsetHigh fields are all used internally by the system and an application should not manipulate or directly use them. The hEvent field, on the other hand, allows an application to associate an event object handle with this operation.

When an overlapped I/O request finally completes, your application is responsible for retrieving the overlapped results. In the event notification method, Winsock will change the event-signaling state of an event object that is associated with a WSAOVERLAPPED structure from non-signaled to signaled when an overlapped request finally completes. Because an event object is assigned to the WSAOVERLAPPED structure, you can easily determine when an overlapped I/O call completes by calling the WSAWaitForMultipleEvents() function, which we also described in the WSAEventSelect() I/O model. WSAWaitForMultipleEvents() waits a specified amount of time for one or more event objects to become signaled. We can't stress this point enough: remember that WSAWaitForMultipleEvents() is capable of waiting on only 64 event objects at a time. Once you determine which overlapped request has completed, you need to determine the success or failure of the overlapped call by calling WSAGetOverlappedResult(), which is defined as:

 

BOOL WSAGetOverlappedResult(

    SOCKET s,

    LPWSAOVERLAPPED lpOverlapped,

    LPDWORD lpcbTransfer,

    BOOL fWait,

    LPDWORD lpdwFlags

);

 

The s parameter identifies the socket that was specified when the overlapped operation was started. The lpOverlapped parameter is a pointer to the WSAOVERLAPPED structure that was specified when the overlapped operation was started. The lpcbTransfer parameter is a pointer to a DWORD variable that receives the number of bytes that were actually transferred by an overlapped send or receive operation. The fWait parameter determines whether the function should wait for a pending overlapped operation to complete. If fWait is TRUE, the function does not return until the operation has been completed. If fWait is FALSE and the operation is still pending, WSAGetOverlappedResult() returns FALSE with the error WSA_IO_INCOMPLETE. Because in our case we waited on a signaled event for overlapped completion, this parameter has no effect. The final parameter, lpdwFlags, is a pointer to a DWORD that will receive resulting flags if the originating overlapped call was made with the WSARecv() or the WSARecvFrom() function.

If the WSAGetOverlappedResult() function succeeds, the return value is TRUE. This means that your overlapped operation has completed successfully and that the value pointed to by lpcbTransfer has been updated. If the return value is FALSE, one of the following statements is true:

 

  1. The overlapped I/O operation is still pending (as we previously described).
  2. The overlapped operation completed, but with errors.
  3. The overlapped operation's completion status could not be determined because of errors in one or more of the parameters supplied to WSAGetOverlappedResult.

 

Upon failure, the value pointed to by lpcbTransfer will not be updated, and your application should call the WSAGetLastError() function to determine the cause of the failure.

The following sample of code demonstrates how to structure a simple server application that is capable of managing overlapped I/O on one socket using the event notification described above.

 

#define DATA_BUFSIZE 4096

void main(void)

{

    WSABUF DataBuf;

    char buffer[DATA_BUFSIZE];

    DWORD EventTotal = 0, RecvBytes=0, Flags=0;

    WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

    WSAOVERLAPPED AcceptOverlapped;

    SOCKET ListenSocket, AcceptSocket;

 

    // Step 1:

    //  Start Winsock and set up a listening socket

    ...

 

    // Step 2:

    //  Accept an inbound connection

    AcceptSocket = accept(ListenSocket, NULL, NULL);

 

    // Step 3:

    //  Set up an overlapped structure

    EventArray[EventTotal] = WSACreateEvent();

 

    ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));

    AcceptOverlapped.hEvent = EventArray[EventTotal];

 

    DataBuf.len = DATA_BUFSIZE;

    DataBuf.buf = buffer;

 

    EventTotal++;

 

    // Step 4:

    //  Post a WSARecv request to begin receiving data on the socket

    if (WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL) == SOCKET_ERROR)

    {

     if (WSAGetLastError() != WSA_IO_PENDING)

     {

         // Error occurred

     }

   }

 

    // Process overlapped receives on the socket

    while(TRUE)

    {

        DWORD    Index;

        // Step 5:

        //  Wait for the overlapped I/O call to complete

        Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);

 

        // Index should be 0 because we

        // have only one event handle in EventArray

 

        // Step 6:

        //  Reset the signaled event

        WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

 

        // Step 7:

        //  Determine the status of the overlapped request

        WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE, &Flags);

 

        // First check to see whether the peer has closed

        // the connection, and if so, close the socket

        if (BytesTransferred == 0)

        {

            printf("Closing socket %d\n", AcceptSocket);

            closesocket(AcceptSocket);

            WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

            return;

        }

 

        // Do something with the received data

        // DataBuf contains the received data

        ...

 

        // Step 8:

        //  Post another WSARecv() request on the socket

        Flags = 0;

        ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));

 

        AcceptOverlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];

 

        DataBuf.len = DATA_BUFSIZE;

        DataBuf.buf = buffer;

 

        if (WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL) == SOCKET_ERROR)

        {

            if (WSAGetLastError() != WSA_IO_PENDING)

            {

                // Unexpected error

            }

        }

    }

}

 

The application outlines the following programming steps:

 

  1. Create a socket and begin listening for a connection on a specified port.
  2. Accept an inbound connection.
  3. Create a WSAOVERLAPPED structure for the accepted socket and assign an event object handle to the structure. Also assign the event object handle to an event array to be used later by the WSAWaitForMultipleEvents() function.
  4. Post an asynchronous WSARecv() request on the socket by specifying the WSAOVERLAPPED structure as a parameter.
  5. Call WSAWaitForMultipleEvents() using the event array and wait for the event associated with the overlapped call to become signaled.
  6. Determine the return status of the overlapped call by using WSAGetOverlappedResult().
  7. Reset the event object by using WSAResetEvent() with the event array and process the completed overlapped request.
  8. Post another overlapped WSARecv() request on the socket.
  9. Repeat steps 5–8.

 

This example can easily be expanded to handle more than one socket by moving the overlapped I/O processing portion of the code to a separate thread and allowing the main application thread to service additional connection requests.

If a Winsock function is called in an overlapped fashion (either by specifying an event within the WSAOVERLAPPED structure or with a completion routine), the operation might complete immediately. For example, calling WSARecv() when data has already been received and buffered causes WSARecv() to return NO_ERROR. If any overlapped function fails with WSA_IO_PENDING or immediately succeeds, the completion event will always be signaled and the completion routine will be scheduled to run (if specified). For overlapped I/O with a completion port, this means that completion notification will be posted to the completion port for servicing.

 

The Overlapped Model Program Example

 

The following program example tries to demonstrate the use of overlapped model. Create a new empty Win32 console mode application and add the project/solution name.

 

The Overlapped IO Model: Program example tries to demonstrate the use of overlapped model - creating a new empty Win32 console mode application and add the project/solution name.

 

Add the following source code.

 

// Description:

//

//    This sample illustrates how to develop a simple echo server Winsock

//    application using the Overlapped I/O model with event notification. This

//    sample is implemented as a console-style application and simply prints

//    messages when connections are established and removed from the server.

//    The application listens for TCP connections on port 5150 and accepts them

//    as they arrive. When this application receives data from a client, it

//    simply echos (this is why we call it an echo server) the data back in

//    it's original form until the client closes the connection.

//

//    Note: There are no command line options for this sample.

// Link to ws2_32.lib

#include <winsock2.h>

#include <windows.h>

#include <stdio.h>

 

#define PORT 5150

#define DATA_BUFSIZE 8192

 

typedef struct _SOCKET_INFORMATION {

   CHAR Buffer[DATA_BUFSIZE];

   WSABUF DataBuf;

   SOCKET Socket;

   WSAOVERLAPPED Overlapped;

   DWORD BytesSEND;

   DWORD BytesRECV;

} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

 

DWORD WINAPI ProcessIO(LPVOID lpParameter);

 

DWORD EventTotal = 0;

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

LPSOCKET_INFORMATION SocketArray[WSA_MAXIMUM_WAIT_EVENTS];

CRITICAL_SECTION CriticalSection;

 

int main(int argc, char **argv)

{

   WSADATA wsaData;

   SOCKET ListenSocket, AcceptSocket;

   SOCKADDR_IN InternetAddr;

   DWORD Flags;

   DWORD ThreadId;

   DWORD RecvBytes;

 

   InitializeCriticalSection(&CriticalSection);

 

   if (WSAStartup((2,2),&wsaData) != 0)

   {

      printf("WSAStartup() failed with error %d\n", WSAGetLastError());

      WSACleanup();

      return 1;

   }

   else

     printf("WSAStartup() looks nice!\n");

 

   if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("Failed to get a socket %d\n", WSAGetLastError());

      return 1;

   }

   else

      printf("WSASocket() is OK lol!\n");

 

   InternetAddr.sin_family = AF_INET;

   InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

   InternetAddr.sin_port = htons(PORT);

 

   if (bind(ListenSocket, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)

   {

      printf("bind() failed with error %d\n", WSAGetLastError());

      return 1;

   }

   else

      printf("YOu see, bind() is working!\n");

 

   if (listen(ListenSocket, 5))

   {

      printf("listen() failed with error %d\n", WSAGetLastError());

      return 1;

   }

   else

      printf("listen() is OK maa...\n");

 

   // Setup the listening socket for connections

   if ((AcceptSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("Failed to get a socket %d\n", WSAGetLastError());

      return 1;

   }

   else

      printf("WSASocket() looks OK!\n");

 

   if ((EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT)

   {

      printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());

      return 1;

   }

   else

      printf("WSACreateEvent() is OK!\n");

 

   // Create a thread to service overlapped requests

   if (CreateThread(NULL, 0, ProcessIO, NULL, 0, &ThreadId) == NULL)

   {

      printf("CreateThread() failed with error %d\n", GetLastError());

      return 1;

   }

   else

      printf("Nothing to say, CreateThread() is OK!\n");

 

   EventTotal = 1;

 

   while(TRUE)

   {

       // Accept inbound connections

      if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) == INVALID_SOCKET)

      {

          printf("accept() failed with error %d\n", WSAGetLastError());

          return 1;

      }

      else

          printf("accept() is OK!\n");

 

      EnterCriticalSection(&CriticalSection);

      // Create a socket information structure to associate with the accepted socket

      if ((SocketArray[EventTotal] = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL)

      {

         printf("GlobalAlloc() failed with error %d\n", GetLastError());

         return 1;

      }

      else

         printf("GlobalAlloc() for LPSOCKET_INFORMATION is pretty fine!\n");

 

      // Fill in the details of our accepted socket

      SocketArray[EventTotal]->Socket = AcceptSocket;

      ZeroMemory(&(SocketArray[EventTotal]->Overlapped), sizeof(OVERLAPPED));

      SocketArray[EventTotal]->BytesSEND = 0;

      SocketArray[EventTotal]->BytesRECV = 0;

      SocketArray[EventTotal]->DataBuf.len = DATA_BUFSIZE;

      SocketArray[EventTotal]->DataBuf.buf = SocketArray[EventTotal]->Buffer;

 

      if ((SocketArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] = WSACreateEvent()) == WSA_INVALID_EVENT)

      {

         printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());

         return 1;

      }

      else

         printf("WSACreateEvent() is OK!\n");

 

      // Post a WSARecv() request to to begin receiving data on the socket

      Flags = 0;

      if (WSARecv(SocketArray[EventTotal]->Socket,

         &(SocketArray[EventTotal]->DataBuf), 1, &RecvBytes, &Flags, &(SocketArray[EventTotal]->Overlapped), NULL) == SOCKET_ERROR)

      {

         if (WSAGetLastError() != ERROR_IO_PENDING)

         {

            printf("WSARecv() failed with error %d\n", WSAGetLastError());

            return 1;

         }

      }

      else

            printf("WSARecv() should be working!\n");

 

      EventTotal++;

      LeaveCriticalSection(&CriticalSection);

 

      // Signal the first event in the event array to tell the worker thread to

      // service an additional event in the event array

      if (WSASetEvent(EventArray[0]) == FALSE)

      {

         printf("WSASetEvent() failed with error %d\n", WSAGetLastError());

         return 1;

      }

      else

         printf("Don't worry, WSASetEvent() is OK!\n");

   }

}

 

DWORD WINAPI ProcessIO(LPVOID lpParameter)

{

   DWORD Index;

   DWORD Flags;

   LPSOCKET_INFORMATION SI;

   DWORD BytesTransferred;

   DWORD i;

   DWORD RecvBytes, SendBytes;

 

   // Process asynchronous WSASend, WSARecv requests

   while(TRUE)

   {

      if ((Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE,  WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED)

      {

         printf("WSAWaitForMultipleEvents() failed %d\n", WSAGetLastError());

         return 0;

      }

      else

         printf("WSAWaitForMultipleEvents() is OK!\n");

 

      // If the event triggered was zero then a connection attempt was made

      // on our listening socket.

      if ((Index - WSA_WAIT_EVENT_0) == 0)

      {

         WSAResetEvent(EventArray[0]);

         continue;

      }

 

      SI = SocketArray[Index - WSA_WAIT_EVENT_0];

      WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

 

      if (WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE || BytesTransferred == 0)

      {

         printf("Closing socket %d\n", SI->Socket);

 

         if (closesocket(SI->Socket) == SOCKET_ERROR)

         {

            printf("closesocket() failed with error %d\n", WSAGetLastError());

         }

         else

            printf("closesocket() is OK!\n");

 

         GlobalFree(SI);

         WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

         // Cleanup SocketArray and EventArray by removing the socket event handle

         // and socket information structure if they are not at the end of the arrays

         EnterCriticalSection(&CriticalSection);

 

         if ((Index - WSA_WAIT_EVENT_0) + 1 != EventTotal)

            for (i = Index - WSA_WAIT_EVENT_0; i < EventTotal; i++)

            {

               EventArray[i] = EventArray[i + 1];

               SocketArray[i] = SocketArray[i + 1];

            }

 

         EventTotal--;

         LeaveCriticalSection(&CriticalSection);

         continue;

      }

      // Check to see if the BytesRECV field equals zero. If this is so, then

      // this means a WSARecv call just completed so update the BytesRECV field

      // with the BytesTransferred value from the completed WSARecv() call.

      if (SI->BytesRECV == 0)

      {

         SI->BytesRECV = BytesTransferred;

         SI->BytesSEND = 0;

      }

      else

      {

         SI->BytesSEND += BytesTransferred;

      }

 

      if (SI->BytesRECV > SI->BytesSEND)

      {

         // Post another WSASend() request.

         // Since WSASend() is not guaranteed to send all of the bytes requested,

         // continue posting WSASend() calls until all received bytes are sent

         ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));

         SI->Overlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];

 

         SI->DataBuf.buf = SI->Buffer + SI->BytesSEND;

         SI->DataBuf.len = SI->BytesRECV - SI->BytesSEND;

 

         if (WSASend(SI->Socket, &(SI->DataBuf), 1, &SendBytes, 0, &(SI->Overlapped), NULL) == SOCKET_ERROR)

         {

            if (WSAGetLastError() != ERROR_IO_PENDING)

            {

               printf("WSASend() failed with error %d\n", WSAGetLastError());

               return 0;

            }

         }

         else

              printf("WSASend() is OK!\n");

      }

      else

      {

         SI->BytesRECV = 0;

         // Now that there are no more bytes to send post another WSARecv() request

         Flags = 0;

         ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));

         SI->Overlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];

 

         SI->DataBuf.len = DATA_BUFSIZE;

         SI->DataBuf.buf = SI->Buffer;

 

         if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &RecvBytes, &Flags, &(SI->Overlapped), NULL) == SOCKET_ERROR)

         {

            if (WSAGetLastError() != ERROR_IO_PENDING)

            {

               printf("WSARecv() failed with error %d\n", WSAGetLastError());

               return 0;

            }

         }

         else

             printf("WSARecv() is OK!\n");

      }

   }

}

 

Build and run the project. The following screenshot shows a sample output which is ready for connection.

 

------------------------------------------------------------------

 

Running the Server and Client Program

 

Firstly we run the server program.

 

The Overlapped IO Model: Program example tries to demonstrate the use of overlapped model - running the program as server/receiver, waiting connection from sender

 

Next, we run the client program several times from different computer.

 

The Overlapped IO Model: Program example tries to demonstrate the use of overlapped model - running the client/sender program twice

 

The previous server sample output is shown below.

 

The Overlapped IO Model: Program example tries to demonstrate the use of overlapped model - the previous server/receiver sample output when communication was completed

 

 

 


< WSAEventSelect Program Example | Winsock2 I/O Method Main | Overlapped & AcceptEx() Example >