WIN网络编程-EventSelectServer模型的服务器设计

//EventSelectServer模型的服务器设计 

/
//EventSelectServer.h文件

//主线程负责监听客户端的连接请求,接受到新连接之后,将新套节字安排给

//工作线程I/O,每个工作线程最多处理64个套节字,如果再有新的套节字,就

//再创建新的工作线程。

//服务器程序每接受到一个新的连接,便为新连接申请了一个SOCKET_OBJ结构,初

//始化该结构的成员。当连接关闭或出错时,释放内存空间。

//当客户数量增加时,服务器就要创建额外的线程去处理I/O。每创建一个线

//程,便为线程申请一个THREAD_OBJ结构,初始化该结构的成员。当客户数量减少

//,处理I/O线程关闭时,再释放内存空间。

//

//HANDLE WINAPI CreateThread(
//  __in_opt  LPSECURITY_ATTRIBUTES lpThreadAttributes,
// __in      SIZE_T dwStackSize,
// __in      LPTHREAD_START_ROUTINE lpStartAddress,
//  __in_opt  LPVOID lpParameter,
// __in      DWORD dwCreationFlags,
//  __out_opt  LPDWORDlpThreadId
//);

//

//

 

DWORD WINAPI ServerThread(LPVOIDlpParam);


// 套节字对象
typedef struct _SOCKET_OBJ
{
 SOCKETs;     //套节字句柄
 HANDLEevent;    //与此套节字相关联的事件对象句柄
 sockaddr_inaddrRemote;  //客户端地址信息

 _SOCKET_OBJ*pNext;   //指向下一个SOCKET_OBJ对象,为的是连成一个表
} SOCKET_OBJ, *PSOCKET_OBJ;

// 线程对象
typedef struct _THREAD_OBJ
{
 HANDLEevents[WSA_MAXIMUM_WAIT_EVENTS]; //记录当前线程要等待的事件对象的句柄
 intnSocketCount;      //记录当前线程处理的套节字的数量 <= WSA_MAXIMUM_WAIT_EVENTS

 PSOCKET_OBJpSockHeader;    //当前线程处理的套节字对象列表,pSockHeader指向表头
 PSOCKET_OBJpSockTail;     //pSockTail指向表尾

 CRITICAL_SECTIONcs;     //关键代码段变量,为的是同步对本结构的访问
 _THREAD_OBJ*pNext;      //指向下一个THREAD_OBJ对象,为的是连成一个表

} THREAD_OBJ, *PTHREAD_OBJ;

// 线程列表
PTHREAD_OBJ g_pThreadList;  //指向线程对象列表表头
CRITICAL_SECTIONg_cs;   //同步对此全局变量的访问


// 状态信息
LONG g_nTatolConnections;  //总共连接数量
LONG g_nCurrentConnections;  //当前连接数量


// 申请一个套节字对象,初始化它的成员
PSOCKET_OBJ GetSocketObj(SOCKET s) 
{
 PSOCKET_OBJ pSocket =(PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
 if(pSocket != NULL)
 {
  pSocket->s =s;
  pSocket->event =::WSACreateEvent();
 }
 return pSocket;
}

// 释放一个套节字对象
void FreeSocketObj(PSOCKET_OBJ pSocket)
{
 ::CloseHandle(pSocket->event);
 if(pSocket->s !=INVALID_SOCKET)
 {
  ::closesocket(pSocket->s);
 }
 ::GlobalFree(pSocket);
}

// 申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中
PTHREAD_OBJ GetThreadObj()
{
 PTHREAD_OBJ pThread =(PTHREAD_OBJ)::GlobalAlloc(GPTR, sizeof(THREAD_OBJ));
 if(pThread != NULL)
 { 
  ::InitializeCriticalSection(&pThread->cs);
  //创建一个事件对象,用于指示该线程的句柄数组需要重组
  pThread->events[0]= ::WSACreateEvent();

  // 将新申请的线程对象添加到列表中
  ::EnterCriticalSection(&g_cs);
  pThread->pNext =g_pThreadList;
  g_pThreadList = pThread;
  ::LeaveCriticalSection(&g_cs);
 }
 return pThread;
}

// 释放一个线程对象,并将它从线程对象列表中移除
void FreeThreadObj(PTHREAD_OBJ pThread)
{
 // 在线程对象列表中查找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是pThread的前一个,即“p->pNext == pThread”
   p->pNext= pThread->pNext;
  }
 }
 ::LeaveCriticalSection(&g_cs);

 // 释放资源
 ::CloseHandle(pThread->events[0]);
 ::DeleteCriticalSection(&pThread->cs);
 ::GlobalFree(pThread);
}

// 重新建立线程对象的events数组
void RebuildArray(PTHREAD_OBJ pThread) 
{
 ::EnterCriticalSection(&pThread->cs);
 PSOCKET_OBJ pSocket =pThread->pSockHeader;
 int n = 1; //从第1个开始写,第0个用于指示需要重建了
 while(pSocket != NULL)
 {
  pThread->events[n++]= pSocket->event;
  pSocket =pSocket->pNext;
 }
 ::LeaveCriticalSection(&pThread->cs);
}

/

// 向一个线程的套节字列表中插入一个套节字
BOOL InsertSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJpSocket)
{
 BOOL bRet = FALSE;
 ::EnterCriticalSection(&pThread->cs);
 if(pThread->nSocketCount< WSA_MAXIMUM_WAIT_EVENTS - 1)
 {
  if(pThread->pSockHeader== NULL)
  {
   pThread->pSockHeader= pThread->pSockTail = pSocket;
  }
  else
  {
   pThread->pSockTail->pNext= pSocket;
   pThread->pSockTail= pSocket;
  }
  pThread->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_OBJpSocket)
{
 ::EnterCriticalSection(&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->events[0]);

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


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)
   {
    charszText[256];
    intnRecv = ::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;
}

PSOCKET_OBJ FindSocketObj(PTHREAD_OBJ pThread, int nIndex) //nIndex从1开始
{
 // 在套节字列表中查找
 PSOCKET_OBJ pSocket =pThread->pSockHeader;
 while(--nIndex)
 {
  if(pSocket == NULL)
   returnNULL;
  pSocket =pSocket->pNext;
 }
 return pSocket;
}

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)    //events[0]受信,重建数组
    {
     RebuildArray(pThread);
     //如果没有客户I/O要处理了,则本线程退出
     if(pThread->nSocketCount== 0)
     {
      FreeThreadObj(pThread);
      return0;
     }
     ::WSAResetEvent(pThread->events[0]);
    }
    else     //处理网络事件
    {
     //查找对应的套节字对象指针,调用HandleIO处理网络事件
     PSOCKET_OBJpSocket = (PSOCKET_OBJ)FindSocketObj(pThread, i);
     if(pSocket!= NULL)
     {
      if(!HandleIO(pThread,pSocket))
       RebuildArray(pThread);
     }
     else
      printf("Unable to find socket object /n ");
    }
   }
  }
 }
 return 0;
}

 

///
// EventSelectServer.cpp文件

#include "../common/initsock.h"

#include<stdio.h>
#include <windows.h>

#include "EventSelectServer.h"

// 初始化Winsock库
CInitSock theSock;

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_insi;
    intnLen = sizeof(si);
    SOCKETsNew = ::accept(sListen, (sockaddr*)&si,&nLen);
    if(sNew== SOCKET_ERROR)
     break;
    PSOCKET_OBJpSocket = GetSocketObj(sNew);
    pSocket->addrRemote= si;
    ::WSAEventSelect(pSocket->s,pSocket->event, FD_READ|FD_CLOSE|FD_WRITE);
    AssignToFreeThread(pSocket);
   }
  }
 }
 ::DeleteCriticalSection(&g_cs);
 return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值