WIN网络编程-重叠I/0服务器设计

///
// OverlappedServer.cpp文件

//基于重叠I/0模型的简单的回显TCP服务器,它接受客户端连接之后,将从客

//户端接收到的数据再发送给客户端。本例采用了单线程。

//使用重叠I/O模型必须使用WSASocket函数创建套接字,传送数据使用

//WSASend、WSARecv、WSARecvFrom

//WSASocket函数创建套接字,这个套接字允许多个I/O同时占用。并使用pBuff

//er->pSocket->nOutstandingOps来统计其上的I/O数量。

//其中SOCKET_OBJ结构用来记录与套接字相关的信息,BUFFER_OBJ记录I/O信息。

//程序每投递一个I/O请求,都要申请一个BUFFER_OBJ对象,使用嵌在对象中

//的OVERLAPPED结构。所有未完成的I/O请求对应的BUFFER_OBJ对象组成了一个

//表,表头指针为全局变量g_pBufferHead。

//提交重叠I/O是重叠模型的关键,通过调用AcceptEx、WSARecv和WSASend函

//数实现。提交这些I/O后,线程便在重叠结构中的事件对象上等等,一旦I/O完成

//事件受信,等待函数返回。

//WSAGetLastError函数报告了WSA_IO_PENDING出错状态,表示I/O操作正在进行。

//AccepEx函数将几个套接字函数的功能集合成在了一起。如果它投递的请求成功完成,则执行了3个操作:

//1、接受了新的连接

//2、新连接的本地地址和远程地址都会返回

//3、接收到了远程主机发来的第一块数据

//

 

 

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

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

CInitSock theSock;

#define BUFFER_SIZE 1024

typedef struct _SOCKET_OBJ
{
 SOCKETs;      //套节字句柄
 intnOutstandingOps;   //记录此套节字上的重叠I/O数量
 
 LPFN_ACCEPTEXlpfnAcceptEx;  //扩展函数AcceptEx的指针(仅对监听套节字而言)
} SOCKET_OBJ, *PSOCKET_OBJ;

//重叠I/O都要提交到特定套节字上。在这些I/O完成之前,对方关闭了连接,

//或者连接发生错误,释放对应的SOCEKET_OBJ对象。

typedef struct _BUFFER_OBJ

 OVERLAPPEDol;   //重叠结构
 char*buff;    //send/recv/AcceptEx所使用的缓冲区
 intnLen;    //buff的长度
 PSOCKET_OBJ pSocket; //此I/O所属的套节字对象

 intnOperation;   //提交的操作类型
#define OP_ACCEPT 1
#define OP_READ  2
#define OP_WRITE 3

 SOCKETsAccept;   //用来保存AcceptEx接受的客户套节字(仅对监听套节字而言)
 _BUFFER_OBJ *pNext;
} BUFFER_OBJ, *PBUFFER_OBJ;

HANDLEg_events[WSA_MAXIMUM_WAIT_EVENTS]; //I/O事件句柄数组
intg_nBufferCount;       //上数组中有效句柄数量
PBUFFER_OBJ g_pBufferHead, g_pBufferTail; //记录缓冲区对象组成的表的地址

// 申请套节字对象和释放套节字对象的函数
PSOCKET_OBJ GetSocketObj(SOCKET s)
{
 PSOCKET_OBJ pSocket =(PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
 if(pSocket != NULL)
 {
  pSocket->s =s;
 }
 return pSocket;
}
void FreeSocketObj(PSOCKET_OBJ pSocket)
{
 if(pSocket->s !=INVALID_SOCKET)
  ::closesocket(pSocket->s);
 ::GlobalFree(pSocket);
}

PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket,ULONG nLen)
{
 if(g_nBufferCount >WSA_MAXIMUM_WAIT_EVENTS - 1)
  return NULL;

 PBUFFER_OBJ pBuffer =(PBUFFER_OBJ)::GlobalAlloc(GPTR, sizeof(BUFFER_OBJ));
 if(pBuffer != NULL)
 {
  pBuffer->buff =(char*)::GlobalAlloc(GPTR, nLen);
  pBuffer->ol.hEvent= ::WSACreateEvent();
  pBuffer->pSocket= pSocket;
  pBuffer->sAccept= INVALID_SOCKET;

  //将新的BUFFER_OBJ添加到列表中
  if(g_pBufferHead == NULL)
  {
   g_pBufferHead= g_pBufferTail = pBuffer;
  }
  else
  {
   g_pBufferTail->pNext= pBuffer;
   g_pBufferTail= pBuffer;
  }
  g_events[++ g_nBufferCount] =pBuffer->ol.hEvent;
 }
 return pBuffer;
}

void FreeBufferObj(PBUFFER_OBJ pBuffer)
{
 // 从列表中移除BUFFER_OBJ对象
 PBUFFER_OBJ pTest = g_pBufferHead;
 BOOL bFind = FALSE;
 if(pTest == pBuffer)
 {
  g_pBufferHead = g_pBufferTail =NULL;
  bFind = TRUE;
 }
 else
 {
  while(pTest != NULL&& pTest->pNext !=pBuffer)
   pTest =pTest->pNext;
  if(pTest != NULL)
  {
   pTest->pNext= pBuffer->pNext;
   if(pTest->pNext== NULL)
    g_pBufferTail= pTest;
   bFind =TRUE;
  }
 }
 // 释放它占用的内存空间
 if(bFind)
 {
  g_nBufferCount --;
  ::CloseHandle(pBuffer->ol.hEvent);
  ::GlobalFree(pBuffer->buff);
  ::GlobalFree(pBuffer); 
 }
}

PBUFFER_OBJ FindBufferObj(HANDLE hEvent)
{
 PBUFFER_OBJ pBuffer = g_pBufferHead;
 while(pBuffer != NULL)
 {
  if(pBuffer->ol.hEvent== hEvent)
   break;
  pBuffer =pBuffer->pNext;
 }
 return pBuffer;
}

void RebuildArray()
{
 PBUFFER_OBJ pBuffer = g_pBufferHead;
 int i =  1;
 while(pBuffer != NULL)
 {
  g_events[i++] =pBuffer->ol.hEvent;
  pBuffer =pBuffer->pNext;
 }
}

BOOL PostAccept(PBUFFER_OBJ pBuffer)
{
 PSOCKET_OBJ pSocket =pBuffer->pSocket;
 if(pSocket->lpfnAcceptEx !=NULL)
 { 
  // 设置I/O类型,增加套节字上的重叠I/O计数
  pBuffer->nOperation= OP_ACCEPT;
  pSocket->nOutstandingOps++;

  //投递此重叠I/O 
  DWORD dwBytes;
  pBuffer->sAccept=
   ::WSASocket(AF_INET,SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
  BOOL b =pSocket->lpfnAcceptEx(pSocket->s,
   pBuffer->sAccept,
   pBuffer->buff,
   BUFFER_SIZE -((sizeof(sockaddr_in) + 16) * 2),
   sizeof(sockaddr_in)+ 16,
   sizeof(sockaddr_in)+ 16,
   &dwBytes,
   &pBuffer->ol);
  if(!b)
  {
   if(::WSAGetLastError()!= WSA_IO_PENDING)
    returnFALSE;
  }
  return TRUE;
 }
 return FALSE;
};

BOOL PostRecv(PBUFFER_OBJ pBuffer)

 // 设置I/O类型,增加套节字上的重叠I/O计数
 pBuffer->nOperation =OP_READ;
 pBuffer->pSocket->nOutstandingOps++;

 // 投递此重叠I/O
 DWORD dwBytes;
 DWORD dwFlags = 0;
 WSABUF buf;
 buf.buf = pBuffer->buff;
 buf.len = pBuffer->nLen;
 if(::WSARecv(pBuffer->pSocket->s,&buf, 1, &dwBytes,&dwFlags,&pBuffer->ol, NULL) !=NO_ERROR)
 {
  if(::WSAGetLastError() !=WSA_IO_PENDING)
   returnFALSE;
 }
 return TRUE;
}

BOOL PostSend(PBUFFER_OBJ pBuffer)
{
 // 设置I/O类型,增加套节字上的重叠I/O计数
 pBuffer->nOperation =OP_WRITE;
 pBuffer->pSocket->nOutstandingOps++;

 // 投递此重叠I/O
 DWORD dwBytes;
 DWORD dwFlags = 0;
 WSABUF buf;
 buf.buf = pBuffer->buff;
 buf.len = pBuffer->nLen;
 if(::WSASend(pBuffer->pSocket->s,
   &buf,1, &dwBytes, dwFlags,&pBuffer->ol, NULL) !=NO_ERROR)
 {
  if(::WSAGetLastError() !=WSA_IO_PENDING)
   returnFALSE;
 }
 return TRUE;
}

BOOL HandleIO(PBUFFER_OBJ pBuffer)
{
 PSOCKET_OBJ pSocket =pBuffer->pSocket; //从BUFFER_OBJ对象中提取SOCKET_OBJ对象指针,为的是方便引用
 pSocket->nOutstandingOps--;

 // 获取重叠操作结果
 DWORD dwTrans;
 DWORD dwFlags;
 BOOL bRet =::WSAGetOverlappedResult(pSocket->s,&pBuffer->ol,&dwTrans, FALSE, &dwFlags);
 if(!bRet)
 {
  //在此套节字上有错误发生,因此,关闭套节字,移除此缓冲区对象。
  //如果没有其它抛出的I/O请求了,释放此缓冲区对象,否则,等待此套节字上的其它I/O也完成
  if(pSocket->s !=INVALID_SOCKET)
  {
   ::closesocket(pSocket->s);
   pSocket->s= INVALID_SOCKET;
  }

  if(pSocket->nOutstandingOps== 0)
   FreeSocketObj(pSocket); 
  
  FreeBufferObj(pBuffer);
  return FALSE;
 }

 // 没有错误发生,处理已完成的I/O
 switch(pBuffer->nOperation)
 {
 case OP_ACCEPT: //接收到一个新的连接,并接收到了对方发来的第一个封包
  {
   //为新客户创建一个SOCKET_OBJ对象
   PSOCKET_OBJpClient = GetSocketObj(pBuffer->sAccept);

   //为发送数据创建一个BUFFER_OBJ对象,这个对象会在套节字出错或者关闭时释放
   PBUFFER_OBJpSend = GetBufferObj(pClient, BUFFER_SIZE); 
   if(pSend ==NULL)
   {
    printf("Too much connections! /n");
    FreeSocketObj(pClient);
    returnFALSE;
   }
   RebuildArray();
   
   //将数据复制到发送缓冲区
   pSend->nLen= dwTrans;
   memcpy(pSend->buff,pBuffer->buff, dwTrans);

   //投递此发送I/O(将数据回显给客户)
   if(!PostSend(pSend))
   {
    //万一出错的话,释放上面刚申请的两个对象
    FreeSocketObj(pSocket); 
    FreeBufferObj(pSend);
    returnFALSE;
   }
   //继续投递接受I/O
   PostAccept(pBuffer);
  }
  break;
 case OP_READ: // 接收数据完成
  {
   if(dwTrans> 0)
   {
    //创建一个缓冲区,以发送数据。这里就使用原来的缓冲区
    PBUFFER_OBJpSend = pBuffer;
    pSend->nLen= dwTrans;
    
    //投递发送I/O(将数据回显给客户)
    PostSend(pSend);
   }
   else //套节字关闭
   {
 
    //必须先关闭套节字,以便在此套节字上投递的其它I/O也返回
    if(pSocket->s!= INVALID_SOCKET)
    {
     ::closesocket(pSocket->s);
     pSocket->s= INVALID_SOCKET;
    }

    if(pSocket->nOutstandingOps== 0)
     FreeSocketObj(pSocket);  
    
    FreeBufferObj(pBuffer);
    returnFALSE;
   }
  }
  break;
 caseOP_WRITE:  // 发送数据完成
  {
   if(dwTrans> 0)
   {
    //继续使用这个缓冲区投递接收数据的请求
    pBuffer->nLen= BUFFER_SIZE;
    PostRecv(pBuffer);
   }
   else //套节字关闭
   {
    //同样,要先关闭套节字
    if(pSocket->s!= INVALID_SOCKET)
    {
     ::closesocket(pSocket->s);
     pSocket->s= INVALID_SOCKET;
    }

    if(pSocket->nOutstandingOps== 0)
     FreeSocketObj(pSocket); 

    FreeBufferObj(pBuffer);
    returnFALSE;
   }
  }
  break;
 }
 return TRUE;
}


void main()
{
 // 创建监听套节字,绑定到本地端口,进入监听模式
 int nPort = 4567;
 SOCKET sListen =
  ::WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);

//plisten可被多个重叠I/O占用
 SOCKADDR_INsi;
 si.sin_family = AF_INET;
 si.sin_port = ::ntohs(nPort);
 si.sin_addr.S_un.S_addr = INADDR_ANY;
 ::bind(sListen, (sockaddr*)&si,sizeof(si));
 ::listen(sListen, 200);

 // 为监听套节字创建一个SOCKET_OBJ对象
 PSOCKET_OBJ pListen = GetSocketObj(sListen);

 // 加载扩展函数AcceptEx
 GUID GuidAcceptEx = WSAID_ACCEPTEX;
 DWORD dwBytes;
 WSAIoctl(pListen->s,
  SIO_GET_EXTENSION_FUNCTION_POINTER,
  &GuidAcceptEx,
  sizeof(GuidAcceptEx),
  &pListen->lpfnAcceptEx,
  sizeof(pListen->lpfnAcceptEx),
  &dwBytes,
  NULL,
  NULL);

 // 创建用来重新建立g_events数组的事件对象
 g_events[0] = ::WSACreateEvent();

 // 在此可以投递多个接受I/O请求
 for(int i=0; i<5; i++)
 {
  PostAccept(GetBufferObj(pListen,BUFFER_SIZE));
 }
 ::WSASetEvent(g_events[0]);
 
 while(TRUE)
 {
  int nIndex =
   ::WSAWaitForMultipleEvents(g_nBufferCount+ 1, g_events, FALSE, WSA_INFINITE, FALSE);
  if(nIndex ==WSA_WAIT_FAILED)
  {
   printf("WSAWaitForMultipleEvents()failed /n");
   break;
  }
  nIndex = nIndex -WSA_WAIT_EVENT_0;
  for(int i=0;i<=nIndex; i++)
  {
   int nRet =::WSAWaitForMultipleEvents(1, &g_events[i], TRUE,0, FALSE);
   if(nRet ==WSA_WAIT_TIMEOUT)
    continue;
   else
   {
    ::WSAResetEvent(g_events[i]);
    //重新建立g_events数组
    if(i== 0)
    {
     RebuildArray();
     continue;
    }

    //处理这个I/O
    PBUFFER_OBJpBuffer = FindBufferObj(g_events[i]);
    if(pBuffer!= NULL)
    {
     if(!HandleIO(pBuffer))
      RebuildArray();
    }
   }
  }
 }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值