基于WSAEventSelect模型的服务器设计

本文参考于《Windows网络与通信程序设计》

基于WSAEventSelect模型的服务器设计

对于WSAEventSelect模型,一个工作线程最多能够处理64个套接字,故采用线程池来管理大于64个套接字的请求。

//定义套接字结构体

typedef struct _SOCKET_OBJ

{

  SOCKET s;          //套接字句柄

  HANDLE event;         //与此套接字相关的事件对象句柄

  sockaddr_in addrRemote;   //客户端地址信息

  _SOCKET_OBJ *pNext;    //指向下一个SOCKET_OBJ对象,形成链表

} SOCKET_OBJ, *PSOCKET_OBJ;

//申请一个套接字对象,初始化它的成员

PSOCKET_OBJ GetSocketObj(SOCKET s)

{

  PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GloblAlloc(GPTR, sizeof(SOCKET_OBJ));

  if (pSocket != NULL)

  {

    pSocket->s = s;

    pSocket->enent = ::WSACreateEvent();

  }

  return pSocket;

}

//释放一个套接字对象

void FreeSocketObj(PSOCKET_OBJ pSocket)

{

  ::CloseHandle(pSocket->event);

  if (pSocket->s != INVALID_SOCKET)

  { ::closesocket(pSocket->s); }

  ::GlobalFree(pSocket);

}

//定义线程结构体

typedef struct _THREAD_OBJ

{

  HANDLE enents[WSA_MAXIMUM_WAIT_EVENTS]; //记录当前线程要等待的事件对象句柄

  int nSocketCount; //记录当前线程处理的套接字数量<= WSA_MAXIMUM_WAIT_EVENTS

  PSOCKET_OBJ pSockHeader; //当前线程处理的套接字对象列表,指向表头

  PSOCKET_OBJ pSockTail;      //指向表尾

  CRITICAL_SECTION cs; //关键代码段变量,为的是同步对本结构对象的访问

  _THREAD_OBJ *pNext; //指向下一个THREAD_OBJ对象,为的是连成一个表

}THREAD_OBJ, *PTHREAD_OBJ;

//全局变量

PTHREAD_OBJ g_pThreadList; //指向线程对象列表表头

CRITICAL_SECTION g_cs;  //同步对此全局变量的访问

//申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中

PTHREAD_OBJ GetThreadObj()

{

  PTHREAD_OBJ pThread = (PTHREAD_OBJ)::GlobalAlloc(GPTR, sizeof(THREAD_OBJ));

  if (pThread != NULL)

  {

    ::InitislizeCriticlSection(&pThread->cs);

    //创建一个事件对象,用于指示该线程的句柄数组需要重建

    pThread->events[0] = ::WSACreateEvent();

    //将申请的线程对象添加到列表

    ::EnterCriticalSection(&g_cs);

    pThread->pNext = g_pThreadList;

    g_pThreadLsit = pThread;  

    ::LeaveCriticalSection(&g_cs);

  } 

  return pThread;

}

//释放一个线程对象,将它从线程对象列表中移除

void FreeThreadObj(PTHREAD_OBJ pThread)

{

  ::EnterCriticalSection(&g_cs);

  PTHREAD_OBJ p = g_pThreadList;

  if (p == pThread ) //是第一个

  { g_pThreadList = p->pNext; }

  else

  {

    while (p != NULL && p->pNext != pThread)

    { p = p->pNext; }

    if (p != NULL)

    {

      p->pNext = pThread->pNext;

    }

  }

  ::LeaveCriticalSection(&g_cs);

  //释放资源

  ::CloseHande(pThread->events[0]);

  ::DeleteCriticalSection(&pThread->cs);

  ::GlobalFree(pThread);

}

//重写建立线程对象的events数组

void RebulidArray(PTHREAD_OBJ pThread)

{

  ::EnterCriticalSection(&pThread->cs);

  PSOCKET_OBJ pSocket = pThread->pSockHeader;

  int n = 1; // 从第一个开始写,第0个用于指示需要重建了

  while(pSocket != NULL)

  {

    pThread->events[n++] = pSocket->event;

    pSocket = pSocket->pNext;

  }

  ::LeaveCriticalSection(&pThread->cs);

}

LONG g_nTatolConnections;   //总共连接数量

LONG g_nCurrentConnections;  //当前连接数量

 

//向一个线程的套接字列表中插入一个套接字

BOOL InsertSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)

{

  BOOL bRet = FALSE;

  ::EnterCriticalSection(&pThread->cs);

  if (pThread->nSocketCount < WSA_MAXIMUM_WAIT_EVENTS -1)

  {

    if (pThread->pSocketHeader == NULL)

    { pThread->pSocketHeader = pThead->pSocketTail = pSocket;}

    else

    {

      pThread->pSockTail->pNext = pSocket;

      pThread->pSockTail = pSocket;

    }

    pThead->nSocketCount++;

    bRet = TRUE;

  }

  ::LeaveCriticalSection(&pThread->cs);

  //插入成功,说明成功处理了客户的连接请求

  if (bRet)

  {

    ::InterlockedIncrement(&g_nTatolConnections);

    ::InterlockedIncrement(&g_nCurrentConnections);

  }

  return bRet;

}

//将一个套接字对象安排给空闲的线程处理

void AssignToFreeThread(PSOCKET_OBJ pSocket)

{

  pSocket->pNext = NULL;

  ::EnterCriticalSection(&g_cs);

  PTHREAD_OBJ pThread = g_pThreadList;

  while (pThread != NULL)

  {

    if (InsertSocketObj(pThread,pSocket)) break;

      pThread = pThread->pNext;

  }

  //没有空闲的线程,为这个套接字创建新的线程

  if (pThread == NULL)

  {

    pThread = GetThreadObj();

    InsertSocketObj(pThread,pSocket);

    ::CreateThread(NULL, 0 , ServerThread, pThread, 0 , NULL);

  }

  ::LeaveCriticalSection(&g_cs);

  //指示线程重建句柄数组

  ::WSASetEvent(pThread->events[0]);

}

//从给定线程的套接字对象列表中移除一个套接字对象

void RemoveSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)

{

  ::EnterCriticakSection(&pThread->cs);

  //在套接字对象列表中查找指定的套接字对象,找到之后将之移除

  PSOCKET_OBJ pTest = pThread->pSockHeader;

  if (pTest == pSocket)

  {

    if (pThread->pSockHeader == pThread->pSockTail)

      pThread->pSockTail = pThread->pSockHeader = pTest->pNext;

    else

      pThread->pSockHeader = pTest->pNext;

  }

  else

  {

    while (pTest != NULL && pTest->pNext != pSocket)

      pTest = pTest->pNext;

    if (pTest != NULL)

    {

      if (pThread->pSockTail == pSocket) pThread->pSockTail = pTest;

      pTest->pNext = pSocket->pNext;

    }

  }

  pThread->nSocketCount--;

  ::LeaveCriticalSection(&pThread->cs);

  ::WSASetEvent(pThread->event[0]); //指示线程重建句柄数组

  ::InterlockedDecrement(&g_nCurrentConnections); //说明一个连接中断

}

//处理I/O的线程

DWORD WINAPI ServerThread(LPVOID lpParam)

{

  //取得本线程对象的指针

  PTHREAD_OBJ pThread = (PTHREAD_OBJ)lpParam;

  while(TRUE)

  {

    //等待网络事件

    int nIndex = ::WSAWaitForMultipleEvents(

        pThread->nSocketCount + 1, pThread->events,FALSE,WSA_INFINITE,FALSE);

    nIndex = nIndex - WSA_WAIT_EVENT_0;

    //查看受信的事件对象

    for (int i = nIndex; i < pThread->nSocketCount + 1; i++)

    {

      nIndex = ::WSAWaitForMultipleEvents(1, &pThread->events[i], TRUE,1000,FALSE);

      if (nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)

        continue;

      else

      {

        if (i == 0) //enents[0]受信,重建数组

        {

          RebuildArray(pThread);

          //如果没有客户I/O处理了,则本线程退出

          if (pThread->nSocketCount == 0)

          {

            FreeThreadObj(pThread);

            return 0;

          }

          ::WSAResetEvent(pThread->events[0]);

        }

        else

        {

          PSOCKET_OBJ pSocket = (PSOCKET_OBJ)FindSocketObj(pThread, i);

          if (pSocket != NULL)

          {

            if (!HandleIO(pThread, pSocket))

              RebuildArray(pThread);

          }

          else

            printf("Unable to find socket object \n");

        }

      }

    }

  }

  return 0;

}

//根据事件对象在events数组中的索引查找相应的套接字对象

PSOCKET_OBJ FindSocketObj(PTHREAD_OBJ pThread, int nIndex) //nIndex从1开始

{

  //在套接字列表中查询

  PSOCKET_OBJ pSocket = pThread->pSockHeader;

  while (--nIndex)

  {

    if (pSocket == NULL)

      return NULL;

    pSocket = pSocket->pNext;

  }  

  return pSocket; 

}

//具体发生的网络事件

BOOL HandleIO(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)

{

  //获取具体发生的网络事件

  WSANETWORKEVENTS event;

  ::WSAEnumNetworkEvents(pSocket->s, pSocket->event,&event);

  do

  {

    if (event.lNetworkEvents & FD_READ) //套接字可读

    {

      if (event.iErrorCode[FD_READ_BIT] == 0)

      {

        char szText[256];

        int nRecv = ::recv(pSocket->s,szText,strlen(szText),0);

        if (nRecv > 0)

        {

          szText[nRecv] = '\0';

          printf("接收到数据: %s \n",szText);

        }

      }

      else

        break;

    } 

  else if (event.lNetworkEvents & FD_CLOSE) //套接字关闭

    break;

  else if (event.lNetworkEvents & FD_WRITE) //套接字可写

  {  

    if (event.iErrorCode[FD_WRITE_BIT] == 0)

    { }

    else

      break;

    return TRUE;

  }

 }while(FALSE);

  RemoveSocketObj(pThread,pSocket);

  FreeSocketObj(pSocket);

  return FALSE;

}

//主线程

int main()

{

  USHORT nPort = 4567; //此服务器监听端口

  SOCKET sListen = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

  sockaddr_in sin;

  sin.sin_family = AF_INET;

  sin.sin_port = htons(nPort);

  sin.sin_addr.S_un.S_addr = INADDR_ANY;

  if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)

  {

    printf("Failed bind() \n");

    return -1;

  }

  ::listen(sListen,200);

  //创建事件对象,并关联到监听的套接字

  WSAEVENT event = ::WSACreateEvent();

  ::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE);

  ::InitializeCriticalSection(&g_cs);

  //处理客户连接请求,打印状态信息

  while(TRUE)

  {

    int nRet = ::WaitForSingleObject(event, 5*1000);

    if (nRet == WAIT_FAILED)

    {

      printf("Failed WaitForSingleObject() \n");

      break;

    }

    else if (nRet == WSA_WAIT_TIMEOUT)

    {

      printf("\n");

      printf(" TatolConnections:%d\n",g_nTatolConnections);

      printf("CurrentConnections:%d\n",g_nCurrentConnections);

      continue;

    }

    else  //有新的连接

    {

      ::ResetEvent(event);

      //循环处理所有的未决的连接请求

      while(TRUE)

      {

        sockaddr_in si;

        int nLen = sizeof(si);

        SOCKET sNew = ::accept(sListen, (sockaddr*)&si, &nLen);

        if (sNew == SOCKET_ERROR)

          break;

        PSOCKET_OBJ pSocket = GetSocketObj(sNew);

        pSocket->addrRemote = si;

        ::WSAEventSelect(pSocket->s, pSocket->evnet, FD_READ|FD_CLOSE|FD_WRITE);

        AssignToFreeThread(pSocket);

      } 

    }

  }

  ::DeleteCriticalSection(&g_cs);

  return 0;

}

原文地址:https://www.cnblogs.com/pbreak/p/1863299.html