《windows网络编程技术》之 Winsock I/O方法(2)

重叠模式 之 基于事件通知的重叠模型

重叠模式是的总体设计以WIN32重叠I/O机制为基础,要想在一个套接字上使用重叠I/O模型,必须使用WSA_FLAG_OVERLAPPED这个标志,如下:

s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);

如果使用的是socket函数,则默认设置为WSA_FLAG_OVERLAPPED。

使用重叠模式,就不得不熟悉WSAOVERLAPPED结构,下面是这个结构的定义:

typedef  struct  WSAOVERLAPPED

{

    DWORD    Internal;

    DWORD    InternalHigh;

    DWORD    Offset;

    DWORD    OffsetHigh;

    WSAEVENT   hEvent;

}  WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;

其中前4个参数都是系统内部调用的,不关我们的事。

WSAOVERLAPPED结构中的hEvent即可和一个事件对象进行关联:用WSACreateEvent创建一个事件对象,将重叠结构的hEvent字段分配给新创建的事件对象。然后调用下列winsock函数(带重叠结构类型的参数),

WSASend 、WSASendTo、 WSARecv、 WSARecvFrom、 WSAIoctl、AcceptEx、 TransimitFile

在对重叠I/O请求完成后,Winsock会更改重叠结构中的事件对象的事件传信状态,从“未传信”-->“已传信”,由于WSACreate最开始在一种未传信的工作状态中,并用一种人工重设模式创建句柄的,所以在I/O请求完成后我们的程序有责任将工作状态从“已传信”转到“未传信”,即调用WSAResetEvent函数。

对重叠I/O请求完成后,我们可以调用WSAGetOverlappedResult函数来判断重叠调用是否成功,定义如下:

BOOL   WSAGetOverlappedResult(

     SOCKET    s,

     LPWSAOVERLAPPED   lpOverlapped,

     LPDWORD      lpcbTransfer,

     BOOL    fWait,

     LPDWORD    lpdwFlags

);

其中,lpcbTransfer对应一个双字变量,负责接收一次重叠发送或接收操作实际传输的字节数。

fWait,用于决定是否应该等待一次重叠操作完成。

lpdwFlags,负责接收结果标志。

下面是一个完整的过程:

1.创建一个套接字,开始在指定的端口上监听连接请求。

2.接收进入的连接请求。

3.为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄,同时把该对象句柄分配给一个事件数组,以便由WSAWaitForMultipleEvent函数使用。

4.在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。

5.使用步骤3中的事件数组,调用WSAWaitForMultipleEvent,等待与重叠调用关联的事件对像进入“已传信”状态(即,等待事件触发)

6.WSAWaitForMultipleEvent函数完成后,针对事件数组,调用WSAResetEvent重设事件对象,并对完成的重叠请求进行处理。

7.使用WSAGetOverlappedResult函数,判断重叠调用的返回状态。

8.在套接字上投递另一个重叠WSARecv请求。

9.重复5--8步骤。

 

 

 

 


代码如下:

#include
#include
#include

#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;  

void main(void)
{
   WSADATA wsaData;
   SOCKET ListenSocket, AcceptSocket;
   SOCKADDR_IN InternetAddr;
   DWORD Flags;
   DWORD ThreadId;
   DWORD RecvBytes;
   INT Ret;

   InitializeCriticalSection(&CriticalSection);

   if ((Ret = WSAStartup(0x0202,&wsaData)) != 0)
   {
      printf("WSAStartup failed with error %d\n", Ret);
      WSACleanup();
      return;
   }

   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;
   }

   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;
   }

   if (listen(ListenSocket, 5))
   {
      printf("listen() failed with error %d\n", WSAGetLastError());
      return;
   }

   // 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;
   }

   if ((EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT)
   {
      printf("WSACreateEvent failed with error %d\n", WSAGetLastError());
      return;
   }

   // 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;
   }

   EventTotal = 1;

   while(TRUE)
   {
       // Accept inbound connections

      if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) == INVALID_SOCKET)
      {
          printf("accept failed with error %d\n", WSAGetLastError());
          return;
      }

      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;
      }

      // 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;
      }

      // 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;
         }
      }

      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;
      }
   }

}


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;
      }

      // 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());
         }

         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 gauranteed 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
      {
         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;
            }
         }
      }
   }
}

 

posted @ 2006-05-27 11:08  红心李  阅读(204)  评论(0编辑  收藏  举报