SocketModel.cpp

 

转自:

http://blog.163.com/wupengzhi2005@126/blog/static/1710100220094715936897/

// DPEventSocket.cpp: implementation of the CSocketModel class.
//
//

#include "stdafx.h"
#include "SocketModel.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//
// Construction/Destruction
//

CSocketModel::CSocketModel()
{
 ::InitializeCriticalSection(&m_csErrorNo);
}

CSocketModel::~CSocketModel()
{
 ::DeleteCriticalSection(&m_csErrorNo);
}

// 设置套接字是否为阻塞的
//入口:套接字,是否需要阻塞的
//出口:如果正确那么返回0,错误返回-1
int CSocketModel::BlockSocket(SOCKET hSocket, BOOL bBlock/*FALSE*/)
{
 u_long IoctlLong = (bBlock) ? 0 : 1;

 if (ioctlsocket(hSocket, FIONBIO, &IoctlLong) == SOCKET_ERROR)
 {
  SetLastError( WSAGetLastError() );
  return (SOCKET_ERROR);
    }
 return (SOCKET_SUCCESS);
}

//设置套接字属性
//入口:套接字
//出口:如果正确那么返回0,错误返回-1
int CSocketModel::SetSocketOption(SOCKET hSocket)
{

    int nActivate = 1;

 //允许地址重用
    if (setsockopt(hSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &nActivate,
  sizeof(nActivate)) == SOCKET_ERROR )
    {
        SetLastError( WSAGetLastError() );
        return (SOCKET_ERROR);//return (-1)
    }

 //  如果支持,设置KEEPALIVE属性 (这样做会带来其他不良后果)
 //setsockopt(hSocket, SOL_SOCKET, SO_KEEPALIVE, (const char *) &nActivate,sizeof(iActivate));

 return (SOCKET_SUCCESS);
}

//接收所有数据,注意在这个函数调用之前必须确认是否有接收消息到来
//入口:套接字,数据缓冲区,缓冲区大小
//出口:如果正确那么返回接收的字节数量,错误返回错误代码
//注意:这里使用的是WINSOCK2的专有WSARecv()函数进行接收,所以为非阻塞模式
//     所以调用此函数前必须确认有数据到来的消息,否则返回错误
int CSocketModel::RecvLL(SOCKET hSocket, char *pszBuffer, int nBufferSize)
{
 DWORD  dwRtxBytes = 0,
    dwRtxFlags = 0;
 WSABUF  WSABuff;

 //清空缓冲
 ZeroMemory(&WSABuff,sizeof(WSABUF));

 WSABuff.len = nBufferSize;
 WSABuff.buf = pszBuffer;
 //如果正确就返回本次接收的字节个数,如果错误返回错误号码(负数)
 return ((WSARecv(hSocket, &WSABuff, 1, &dwRtxBytes, &dwRtxFlags,NULL, NULL) 
  == SOCKET_SUCCESS) ? (int) dwRtxBytes : -WSAGetLastError());
}

int CSocketModel::RecvData_Block(SOCKET hSocket, char *pszBuffer, int nBufferSize, 
  DWORD dwTimeout)
{
 ASSERT(hSocket != NULL);
 if(hSocket==NULL)
  return ( SOCKET_ERROR );
 FD_SET fd = {1, hSocket};
 TIMEVAL tv = {dwTimeout, 0};
 int nBytesReceived=0;
 if(select(0, &fd, NULL, NULL, &tv) == 0) 
  goto CLEAR;
 if((nBytesReceived = recv(hSocket, pszBuffer, nBufferSize, 0)) == SOCKET_ERROR)
  goto CLEAR;
 return nBytesReceived;

CLEAR:
 SetLastError(WSAGetLastError());//超时
 return(SOCKET_ERROR);
}

// 接收数据(阻塞直至收到数据为止)
//入口:套接字,缓冲区,缓冲区大小,超时
//注意:这个函数只管理一个端口的接收信息需求,所以这里的EventSelect不能一起管理64
//     个端口的信息
int CSocketModel::RecvData_Event(SOCKET hSocket, char *pszBuffer, 
          int nBufferSize, DWORD dwTimeout)
{
 HANDLE hReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 if (hReadEvent == NULL)
 {
  SetLastError( (int)GetLastError() );
  return ( SOCKET_ERROR );
 }

 int  nRecvBytes = 0;
 DWORD dwWaitResult ;
 for (;;)
 {
  // 注册FD_READ | FD_CLOSE 事件 
  // (因为可能在等待FD_READ事件中,对方关闭套接字,所以要关注FD_CLOSE)
  if( WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, FD_READ | FD_CLOSE) 
   == SOCKET_ERROR)
  {
   CloseHandle(hReadEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
 
  // 等等FD_READ | FD_CLOSE事件的发生
  dwWaitResult = WSAWaitForMultipleEvents(1, &hReadEvent, TRUE,dwTimeout, TRUE);

  if (dwWaitResult != WSA_WAIT_EVENT_0)
  {
   // 清除事件
   WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
   CloseHandle(hReadEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  
  // 
  /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
  ///   进一步检查网络是否发生错误
  ///
  WSANETWORKEVENTS NetEvent;
  if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hReadEvent,&NetEvent) == SOCKET_ERROR)
  {
   // 清除事件
   WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
   CloseHandle(hReadEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  //判断发生了什么事件 FD_READ 或 FD_CLOSE 
  if( ( NetEvent.lNetworkEvents == FD_CLOSE ) ||
    ( NetEvent.lNetworkEvents == FD_READ && 
      NetEvent.iErrorCode[FD_READ_BIT] !=0 ) ) // 发生错误
  {
   // 清除事件
   WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
   CloseHandle(hReadEvent);
   SetLastError(WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  
  // 清除事件
  WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
 
  // 接收数据
  if ((nRecvBytes = RecvLL(hSocket, pszBuffer, nBufferSize)) >= 0)
   break; // 跳出循环

  //Recv返回的是错误代码的负数,所以需要调转过来
  int nErrorCode = -nRecvBytes;

  if ( nErrorCode != WSAEWOULDBLOCK ) //太多的未完成重叠操作
  {
   CloseHandle(hReadEvent);
   SetLastError( nErrorCode );
   return (SOCKET_ERROR);
  }
  //阻塞住了
  
  //  如果发生阻塞,就等待一定时间后重试,以免CPU轮询浪费时间
  
  Sleep(_BLOCKED_SNDRCV_SLEEP);
    }
    CloseHandle(hReadEvent);
    return (nRecvBytes);
}

int CSocketModel::RecvDataFrom_Block( SOCKET hSocket, struct sockaddr * pFrom, 
         int nAddrlen,char *pszBuffer, int nBufferSize,
         DWORD dwTimeout)
{
 ASSERT(hSocket != NULL);
 if(hSocket==NULL)
  return (SOCKET_ERROR);
 FD_SET fd = {1, hSocket};
 TIMEVAL tv = {dwTimeout, 0};
 int nFromSize=0;
 int nBytesReceived=0;
 if(select(0, &fd, NULL, NULL, &tv) == 0) 
  goto CLEAR;
 nFromSize = nAddrlen;
 nBytesReceived = recvfrom(hSocket, pszBuffer, nBufferSize, 0, pFrom, &nFromSize);
 if(nBytesReceived == SOCKET_ERROR) 
  goto CLEAR;
 return nBytesReceived;

CLEAR:
 SetLastError(WSAGetLastError());//超时
 return(SOCKET_ERROR);
}


// 数据报接收函数
int CSocketModel::RecvDataFrom_Event( SOCKET hSocket, struct sockaddr * pFrom, 
           int nAddrlen,char *pszBuffer, 
           int nBufferSize,DWORD dwTimeout)
{
 HANDLE hReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 if (hReadEvent == NULL)
 {
  SetLastError((int)GetLastError() );
  return (SOCKET_ERROR);
 }

 DWORD  dwRtxBytes = 0,
    dwRtxFlags = 0;
 WSABUF  WSABuff;

 ZeroMemory(&WSABuff,sizeof(WSABUF));
 WSABuff.len = nBufferSize;
 WSABuff.buf = pszBuffer;

 for (;;)
 {
  // 注册FD_READ事件
  if( WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, FD_READ) 
   == SOCKET_ERROR)
  {
   CloseHandle(hReadEvent);
   SetLastError(  WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  DWORD dwWaitResult = WSAWaitForMultipleEvents(1, &hReadEvent, 
   TRUE,dwTimeout, TRUE);

  if( dwWaitResult != WSA_WAIT_EVENT_0 )
  {
   // 注销事件
   WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
   CloseHandle(hReadEvent);
   SetLastError( WSAGetLastError());
   return (SOCKET_ERROR);
  }

  // 
  /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
  ///   进一步检查网络是否发生错误
  ///
  WSANETWORKEVENTS NetEvent;
  if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hReadEvent,&NetEvent)
   == SOCKET_ERROR)
  {
   // 注销事件
   WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
   CloseHandle(hReadEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  if(NetEvent.iErrorCode[FD_READ_BIT] !=0 ) // 发生错误
  {
   // 注销事件
   WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);
   CloseHandle(hReadEvent);
   SetLastError(NetEvent.iErrorCode[FD_READ_BIT]);
   return (SOCKET_ERROR);
  }
  
  // 注销事件
  WSAEventSelect(hSocket, (WSAEVENT) hReadEvent, 0);

  int FromLen = nAddrlen;
  if ( WSARecvFrom(hSocket, &WSABuff, 1, &dwRtxBytes, 
   &dwRtxFlags,pFrom, &FromLen, NULL, NULL) == SOCKET_SUCCESS )
   break;

  if ( WSAGetLastError() != WSAEWOULDBLOCK)
  {
   CloseHandle(hReadEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }

  ///
  // 睡眠一段时间
  //
  Sleep(_BLOCKED_SNDRCV_SLEEP);
 }
 CloseHandle(hReadEvent);
 return ((int) dwRtxBytes);
}

//发送数据,阻塞
//入口:套接字,发送的字串,字串长度,超时值
//出口:正确返回发送的字节数量,错误返回SOCKET_ERROR
int CSocketModel::Send_Block(SOCKET hSocket,char const * pszBuffer, 
  int nBufferSize, DWORD dwTimeout)
{
 ASSERT(hSocket!=NULL);
 if(hSocket==NULL||pszBuffer==NULL)
  return (SOCKET_ERROR);
 FD_SET fd = {1, hSocket};
 TIMEVAL tv = {dwTimeout, 0};
 int nBytesSent=0;
 if(select(0, NULL, &fd, NULL, &tv) == 0)
  goto CLEAR;//选择发送超时
 if((nBytesSent = send(hSocket, pszBuffer, nBufferSize, 0)) == SOCKET_ERROR) 
  goto CLEAR;//发送出错误
 return nBytesSent;

CLEAR:
 SetLastError(WSAGetLastError());//超时
 return(SOCKET_ERROR);
}

//发送全部缓冲区中数据,阻塞
//入口:套接字,发送的字串,字串长度,超时值
//出口:正确返回发送的字节数量,错误返回SOCKET_ERROR
int CSocketModel::SendData_Block(SOCKET hSocket,char const * pszBuffer, 
  int nBufferSize, DWORD dwTimeout)

{
 if(hSocket==NULL)
  return(SOCKET_ERROR);
 int nBytesSent = 0;
 int nBytesThisTime;
 const char* pszTemp = pszBuffer;
 do {
  nBytesThisTime = Send_Block(hSocket,pszTemp, nBufferSize-nBytesSent, dwTimeout);
  if(nBytesThisTime<0)
   return(SOCKET_ERROR);
  //如果一次没有发送成功
  nBytesSent += nBytesThisTime;
  //改变当前字符指针
  pszTemp += nBytesThisTime;
 } while(nBytesSent < nBufferSize);
 return nBytesSent;
}

// 发送数据
//入口:套接字,发送缓冲区,大小,超时
//出口:返回一次发送数据的字节数量,如果错误返回SOCKET_ERROR
int CSocketModel::Send_Event(SOCKET hSocket, char const * pszBuffer, 
          int nBufferSize, DWORD dwTimeout)
{

 HANDLE hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

 if (hWriteEvent == NULL)
 {
  SetLastError( (int)GetLastError() );
  return (SOCKET_ERROR);
 }

 int nSendBytes = 0;

 for (;;)
 {
  
  // 发送数据成功
  if ((nSendBytes = SendLL(hSocket, pszBuffer, nBufferSize)) >= 0)
   break;

  //如果发送的字节数量小于0证明出错
  int nErrorCode = -nSendBytes;
  //如果是WSAEWOULDBLOCK错误,证明在该端口上仍然进行着其他I/O操作
  //所以我们下面进行必要的等待循环处理
  if (nErrorCode != WSAEWOULDBLOCK)
  {
   CloseHandle(hWriteEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }

  ///
  //  睡眠一段时间
  ///
  Sleep(_BLOCKED_SNDRCV_SLEEP);

  // 注册FD_WRITE | FD_CLOSE 事件
  if( WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, FD_WRITE|FD_CLOSE) 
   == SOCKET_ERROR)
  {
   CloseHandle(hWriteEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }

  // 等待事件发生
  DWORD dwWaitResult = WSAWaitForMultipleEvents(1, 
   &hWriteEvent, TRUE,dwTimeout, TRUE);

  if (dwWaitResult != WSA_WAIT_EVENT_0)
  {
   // 清除网络事件
   WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   CloseHandle(hWriteEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }

  // 
  /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
  ///   进一步检查网络是否发生错误
  ///
  WSANETWORKEVENTS NetEvent;
  if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hWriteEvent,&NetEvent) == SOCKET_ERROR)
  {
     // 清除网络事件
   WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   CloseHandle(hWriteEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  if( ( NetEvent.lNetworkEvents == FD_CLOSE ) ||
    ( NetEvent.lNetworkEvents == FD_WRITE   &&
    NetEvent.iErrorCode[FD_WRITE_BIT] !=0 ) ) // 发生错误
  {
   // 清除网络事件
   WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   CloseHandle(hWriteEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  // 清除网络事件
  WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   }

    CloseHandle(hWriteEvent);
    return (nSendBytes);
}

//发送完所有数据或超时
//入口:套接字,缓冲区,缓冲区大小,超时
//出口:返回已经发送的字节个数或SOCKET_ERROR
int CSocketModel::SendData_Event(SOCKET hSocket, char const * pszBuffer, 
     int nBufferSize, DWORD dwTimeout)
{

 int nRtxBytes = 0;
 int nRtxCurrent = 0;

 while (nRtxBytes < nBufferSize)
 {
  nRtxCurrent = Send_Event(hSocket, (pszBuffer + nRtxBytes),
   (nBufferSize - nRtxBytes), dwTimeout);
  if (nRtxCurrent < 0)
   return (nRtxBytes);
  nRtxBytes += nRtxCurrent;
 }
 return (nRtxBytes);
}

//一次发送数据,但不一定全部都发送
//入口:套接字,接收方地址信息,地址结构,结构长度,缓冲区,缓冲区长度,超时
//出口:正确返回发送字节数量,错误返回SOCKET_ERROR
int CSocketModel::SendTo_Block(SOCKET hSocket, const struct sockaddr * pTo,
  int nAddrLen,char const * pszBuffer, int nBufferSize, DWORD dwTimeout)
{
 if(hSocket==NULL||pszBuffer==NULL)
  return SOCKET_ERROR;
 FD_SET fd = {1, hSocket};
 TIMEVAL tv = {dwTimeout, 0};
 int nBytesSent=0;
 if(select(0, NULL, &fd, NULL, &tv) == 0) 
  goto CLEAR;
 nBytesSent = sendto(hSocket, pszBuffer, nBufferSize, 0, pTo, nAddrLen);
 if(nBytesSent == SOCKET_ERROR)
  goto CLEAR;
 return nBytesSent;

CLEAR:
 SetLastError(WSAGetLastError());//超时
 return(SOCKET_ERROR);
}

// 数据报发送数据报
int CSocketModel::SendTo_Event(SOCKET hSocket, const struct sockaddr * pTo,
         int nAddrLen,char const * pszBuffer, 
         int nBufferSize, DWORD dwTimeout)
{
 HANDLE hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 if (hWriteEvent == NULL)
 {
  SetLastError( (int)GetLastError() );
  return (SOCKET_ERROR);
 }

 DWORD dwRtxBytes = 0,
            dwRtxFlags = 0;
 WSABUF WSABuff;

 ZeroMemory(&WSABuff,sizeof(WSABUF));
 WSABuff.len = nBufferSize;
 WSABuff.buf = (char *) pszBuffer;

 for (;;)
 {
  if (WSASendTo( hSocket, &WSABuff, 1, &dwRtxBytes, dwRtxFlags,
   pTo, nAddrLen, NULL, NULL) == SOCKET_SUCCESS)
   break;

  if (WSAGetLastError() != WSAEWOULDBLOCK)
  {
   CloseHandle(hWriteEvent);
   SetLastError(  WSAGetLastError() );
   return (SOCKET_ERROR);
  }

  //
  // 睡眠一段时间
  /
  Sleep(_BLOCKED_SNDRCV_SLEEP);

  // 注册FD_WRITE事件  
  if( WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, FD_WRITE) 
   == SOCKET_ERROR)
  {
   CloseHandle(hWriteEvent);
   SetLastError( WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  DWORD dwWaitResult = WSAWaitForMultipleEvents
   (1, &hWriteEvent, TRUE,dwTimeout, TRUE);
  
  if( dwWaitResult != WSA_WAIT_EVENT_0 )
  {
   // 注销事件
   WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   CloseHandle(hWriteEvent);
   SetLastError(  WSAGetLastError() );
   return (SOCKET_ERROR);
  }

  // 
  /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
  ///   进一步检查网络是否发生错误
  ///
  WSANETWORKEVENTS NetEvent;
  if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hWriteEvent,&NetEvent) 
   == SOCKET_ERROR)
  {
   // 注销事件
   WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   CloseHandle(hWriteEvent);
   SetLastError(  WSAGetLastError() );
   return (SOCKET_ERROR);
  }
  if(NetEvent.iErrorCode[FD_WRITE_BIT] !=0 ) // 发生错误
  {
   // 注销事件
   WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
   CloseHandle(hWriteEvent);
   SetLastError(NetEvent.iErrorCode[FD_WRITE_BIT]);
   return (SOCKET_ERROR);
  }
  
  // 注销事件
  WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
 }

 CloseHandle(hWriteEvent);
 return ((int) dwRtxBytes);
}

 

//一次性发送数据(事件IO)
//入口:套接字,缓冲区,缓冲区长度
//出口:正确-返回发送的字节数量,错误-返回错误代码的负数
int CSocketModel::SendLL(SOCKET hSocket, char const * pszBuffer, 
        int nBufferSize)
{
 DWORD dwRtxBytes = 0;
 WSABUF WSABuff;

 ZeroMemory(&WSABuff,sizeof(WSABUF));
 WSABuff.len = nBufferSize;
 WSABuff.buf = (char *) pszBuffer;

 return ((WSASend(hSocket, &WSABuff, 1, &dwRtxBytes, 0,NULL, NULL)
  == SOCKET_SUCCESS) ? (int) dwRtxBytes : -WSAGetLastError());
}

//关闭套接字
//入口:套接字,是否强行关闭(如果bHardClose==FALSE,那么接收剩余的数据后关闭连接)
//注意:这里的接收剩余数据只是为了让SERVER将数据发送操作执行完毕
//     所以接收的剩余数据不可用
void CSocketModel::CloseSocket(SOCKET hSocket, BOOL bHardClose)
{
 // 不需要捕获错误
 if (!bHardClose) // 优雅关闭 Graceful close
 {
  // 不再发送数据,对于TCP套接字,在所有的数据都发送完毕之后,
  // 将发送一个 FIN ,通知接收方所有数据已经发送完毕。
  shutdown(hSocket, SD_SEND);

  // 接收缓冲区有可能还有未接收的数据,在关闭套接字之前应该先
  // 读取残留的数据。
  int  nRecvResult;
  HANDLE hSocketEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  //为残留数据提供的缓冲区
  char szBuffer[256];
  do
  {
   if (hSocketEvent != NULL)
   {
    //注册网络事件
    WSAEventSelect(hSocket, (WSAEVENT) hSocketEvent, FD_READ | FD_CLOSE);
    WSAWaitForMultipleEvents(1, &hSocketEvent, TRUE,_SHUTDOWN_RECV_TIMEOUT, TRUE);
    //清除网络事件
    WSAEventSelect(hSocket, (WSAEVENT) hSocketEvent, 0);
   }
   ZeroMemory(szBuffer,256);
   //接收残留数据
   nRecvResult = RecvLL(hSocket, szBuffer, sizeof(szBuffer));
  } while (nRecvResult > 0);

  if (hSocketEvent != NULL)
   CloseHandle(hSocketEvent);
  //不再允许接收和发送
  shutdown(hSocket, SD_BOTH);
 }
 // 关闭套接字
 closesocket(hSocket);
}

//接受套接字连接
//入口:侦听端口号,本地地址
//出口:返回一个可用的套接字或INVALID_SOCKET
//注意:如果为阻塞等待,就是在DWTIMEOUT的时间设置为无限的时候
//     如果我们关闭该套接字会出现的问题
SOCKET CSocketModel::Accept_Event(SOCKET hSocket, struct sockaddr * pSocketAddress, 
           int *nAddrLen,DWORD dwTimeout
           /*= DP_DEFAULT_TIMEOUT*/)
{
 HANDLE hAcceptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 if (hAcceptEvent == NULL)
 {
  SetLastError( (int)GetLastError() );
  return (INVALID_SOCKET);
 }

 //异步事件选择
 // 注册FD_ACCEPT事件
 if( WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, FD_ACCEPT) == SOCKET_ERROR)
 {
  CloseHandle(hAcceptEvent);
  SetLastError( WSAGetLastError() );
  return (INVALID_SOCKET);
 }

 //进行非阻塞接收
 SOCKET hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
 int nConnectError = WSAGetLastError();
 //如果返回的端口是没有定义的,并且接收的错误码为"代决"状态
 if ((hSocketAccept == INVALID_SOCKET) && (nConnectError == WSAEWOULDBLOCK))
 {

  //阻塞,这个函数如果正确返回那么我们在使用WSAACCEPT重新接收一次,
  //就可以获得用户的SOCKET
  //因为这个函数返回代表ACCPETEVENT成为传信状态,如果网络没有错误那么
  //代表有用户的正确端口接入
  DWORD dwWaitResult = WSAWaitForMultipleEvents
   (1,&hAcceptEvent,TRUE,dwTimeout,TRUE);
  if (dwWaitResult == WSA_WAIT_EVENT_0)
  {
   // 
   /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
   ///   进一步检查网络是否发生错误
   ///
   WSANETWORKEVENTS NetEvent;
   if(WSAEnumNetworkEvents(hSocket,hAcceptEvent,&NetEvent) == SOCKET_ERROR)
    SetLastError( WSAGetLastError() );
   else if(NetEvent.iErrorCode[FD_ACCEPT_BIT] !=0 ) // 发生错误
    SetLastError( NetEvent.iErrorCode[FD_ACCEPT_BIT] );
   else
    //接收到真正的用户SOCKET
    hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
  }
  else
   SetLastError(WSAGetLastError());
 }
 
 // 注销网络事件
 WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, 0);
 CloseHandle(hAcceptEvent);

 if (hSocketAccept != INVALID_SOCKET)
 {
  // 设置套接字的属性为地址可重用并且为非阻塞的
  if ((BlockSocket(hSocketAccept, 0)  == SOCKET_ERROR ) ||
    (SetSocketOption(hSocketAccept) == SOCKET_ERROR ) )
  {
   CloseSocket(hSocketAccept,TRUE);
   return (INVALID_SOCKET);
  }
 }
 return (hSocketAccept);
}

// 接受套接字连接(允许中断)
//入口:结束事件,其他入口都和Accept一样
//出口:返回一个可用的套接字或INVALID_SOCKET
//注意:超时值默认为DP_DEFAULT_TIMEOUT,如果结束事件没有的话,
//     那么跟调用上面的函数没有区别
SOCKET CSocketModel::AcceptEx_Event(SOCKET hSocket, struct sockaddr * pSocketAddress, 
           int *nAddrLen,HANDLE hEndEvent,DWORD dwTimeout 
           /*= DP_DEFAULT_TIMEOUT*/)
{
 if( hEndEvent == NULL)
  return Accept_Event(hSocket,pSocketAddress,nAddrLen,dwTimeout);

 HANDLE hAcceptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 if (hAcceptEvent == NULL)
 {
  SetLastError( (int)GetLastError() );
  return (INVALID_SOCKET);
 }

 WSAEVENT hEvent[2]; 
 hEvent[0] = (WSAEVENT)hAcceptEvent;
 hEvent[1] = (WSAEVENT)hEndEvent;

 // 注册FD_ACCEPT事件
 if( WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, FD_ACCEPT) == SOCKET_ERROR)
 {
  CloseHandle(hAcceptEvent);
  SetLastError( WSAGetLastError() );
  return (INVALID_SOCKET);
 }

 SOCKET hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
 int    nConnectError = WSAGetLastError();

 if ((hSocketAccept == INVALID_SOCKET) && (nConnectError == WSAEWOULDBLOCK))
 {
  // 阻塞,等待两个事件,如果其中一个发生那么我们向下继续执行
  //所以这意味着指定结束事件后,如果程序想中断ACCEPT的执行
  //那么就激活该结束事件,程序将按照错误一样的处理
  DWORD dwWaitResult = WSAWaitForMultipleEvents(2, hEvent, FALSE,dwTimeout, TRUE);
  if (dwWaitResult == WSA_WAIT_EVENT_0)
  {
   // 
   /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
   ///   进一步检查网络是否发生错误
   ///
   WSANETWORKEVENTS NetEvent;
   if(WSAEnumNetworkEvents(hSocket,hAcceptEvent,&NetEvent) == SOCKET_ERROR)
    SetLastError(WSAGetLastError());
   else if(NetEvent.iErrorCode[FD_ACCEPT_BIT] !=0 ) // 发生错误
    SetLastError( NetEvent.iErrorCode[FD_ACCEPT_BIT] );
   else
    hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
  }
  else
   SetLastError( WSAGetLastError() );
 }
 
 // 注销网络事件
 WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, 0);
 CloseHandle(hAcceptEvent);

 if (hSocketAccept != INVALID_SOCKET)
 {
  // 设置套接字的属性为地址可重用并且为非阻塞的
  if ((BlockSocket(hSocketAccept, 0) < 0) ||
    (SetSocketOption(hSocketAccept) < 0) )
  {
   CloseSocket(hSocketAccept,TRUE);
   return (INVALID_SOCKET);
  }
 }
 return (hSocketAccept);
}

//阻塞ACCEPT,没有响应不返回
//入口:套接字,主机地址,长度
//出口:正确返回端口号,否则返回INVALID_SOCKET
SOCKET CSocketModel::Accept_Block(SOCKET hSocket, struct sockaddr * pSocketAddress,
         int *nAddrLen)
{
 ASSERT(hSocket!=NULL);
 //int nLengthAddr = sizeof(SOCKADDR);
 SOCKET hAccept = accept(hSocket, pSocketAddress, nAddrLen);
 //如果该端口错误
 if(hAccept == INVALID_SOCKET) {
  SetLastError(WSAGetLastError());
 }
 return hAccept;
}

// 绑定套接字
//入口:套接字,绑定的地址信息,长度
//出口:正确0,错误-1
int CSocketModel::BindSocket(SOCKET hSocket, struct sockaddr * pSocketAddress, 
        int nAddrLen)
{
 if (bind(hSocket, pSocketAddress, nAddrLen) == SOCKET_ERROR)
 {
  SetLastError( WSAGetLastError() );
  return (SOCKET_ERROR);
 }
 return (SOCKET_SUCCESS);
}

// 绑定套接字
//入口:套接字,端口号
//出口:正确0,错误-1
//注意:这个函数不同于上个函数,调用这个函数只需要给定一个端口号
//     至于地址信息在函数内部默认处理(本机IP的该端口)
int CSocketModel::BindSocketEx(SOCKET hSocket,int nPort)
{
 SOCKADDR_IN sockAddr;
 ZeroMemory(&sockAddr,sizeof(sockAddr));

 sockAddr.sin_family   = AF_INET;
 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
 sockAddr.sin_port   = htons((u_short)nPort);

 return BindSocket(hSocket,(SOCKADDR *)&sockAddr, sizeof(sockAddr));
}

//释放Winsock2动态连接库
//入口:无
//出口:无
void CSocketModel::CleanupLibrary(void)
{
    WSACleanup();
}

//注册WINSOCK2.2DLL
//入口:无
//出口:无
int CSocketModel::InitLibrary(void)
{
 WSADATA         WSD;
 WORD wVersionRequired = MAKEWORD( _SOCKET_MAJOR_VERSION,_SOCKET_MINOR_VERSION );
 
 ZeroMemory(&WSD,sizeof(WSADATA));

 int nErrorNo = WSAStartup(wVersionRequired, &WSD);

 if ( SOCKET_SUCCESS != nErrorNo )
 {
  SetLastError( nErrorNo );
  return ( SOCKET_ERROR );
 }
 if ( LOBYTE( WSD.wVersion ) != _SOCKET_MINOR_VERSION ||
   HIBYTE( WSD.wVersion ) != _SOCKET_MAJOR_VERSION ) 
 {
  WSACleanup( );
  SetLastError( WSAVERNOTSUPPORTED );
  return (SOCKET_ERROR); 
 }
 //成功初始化
 return (SOCKET_SUCCESS);
}


// 建立连接
//入口:套接字,地址结构,结构长度,超时
//出口:SOCKET_SUCCESS/SOCKET_ERROR
int CSocketModel::Connect_Event(SOCKET hSocket, const struct sockaddr * pSocketAddress, 
         int nAddrLen,DWORD dwTimeout)
{

 HANDLE hConnectEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

 if (hConnectEvent == NULL)
 {
  SetLastError( (int)GetLastError() );
  return (SOCKET_ERROR);
 }

 // 注册FD_CONNECT事件
 if( WSAEventSelect(hSocket, (WSAEVENT) hConnectEvent, FD_CONNECT) == SOCKET_ERROR)
 {
  CloseHandle(hConnectEvent);
  SetLastError( WSAGetLastError() );
  return (SOCKET_ERROR);
 }

 int nConnectResult = WSAConnect(hSocket, pSocketAddress, nAddrLen, 
  NULL, NULL, NULL, NULL);
 int nConnectError  = WSAGetLastError();
 
 if ((nConnectResult == SOCKET_ERROR) && (nConnectError == WSAEWOULDBLOCK))
 {
  DWORD dwWaitResult = WSAWaitForMultipleEvents(1, &hConnectEvent, 
   TRUE,dwTimeout, TRUE);
  
  if (dwWaitResult != WSA_WAIT_EVENT_0)
  {
   SetLastError( WSAGetLastError() );
   nConnectResult = SOCKET_ERROR;
  }
  else
  {
   // 
   /// 注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
   ///   进一步检查网络是否发生错误
   ///
   WSANETWORKEVENTS NetEvent;
   if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hConnectEvent,&NetEvent) 
    == SOCKET_ERROR)
   {
    SetLastError( WSAGetLastError() );
    nConnectResult = SOCKET_ERROR;
   }
   else if(NetEvent.iErrorCode[FD_CONNECT_BIT] !=0 ) // 发生错误
   {
    SetLastError( NetEvent.iErrorCode[FD_CONNECT_BIT] );
    nConnectResult = SOCKET_ERROR;
   }
   else
    nConnectResult = SOCKET_SUCCESS;
   
  }
 }

 // 注销网络事件
 WSAEventSelect(hSocket, (WSAEVENT) hConnectEvent, 0);
 CloseHandle(hConnectEvent);
 return (nConnectResult);
}

int CSocketModel::Connect_Block(SOCKET hSocket, const struct sockaddr * pSocketAddress, 
         int nAddrLen)
{
 ASSERT(hSocket!=NULL);
 if(hSocket==NULL)
  return SOCKET_ERROR;
 if(connect(hSocket, pSocketAddress, nAddrLen) == SOCKET_ERROR) 
  return SOCKET_ERROR;
 return SOCKET_SUCCESS;
}

//创建具有重叠IO能力的套接字
//入口:协议,协议类型(TCP/UDP),协议
//出口:返回创建的重叠IO SOCKET
//注意:使用SOCKET()函数创建的套接字默认具有重叠IO能力
SOCKET CSocketModel::CreateSocket(int nAddressFamily /*= AF_INET*/, 
           int nType/*= SOCK_STREAM*/,
           int nProtocol/*= 0*/)
{
 SOCKET hSocket = WSASocket(nAddressFamily, nType, nProtocol, 
  NULL,0,WSA_FLAG_OVERLAPPED); 
 if ( hSocket == INVALID_SOCKET )
 {
  SetLastError( WSAGetLastError() );
  return (INVALID_SOCKET);
 }

 //设置套接字选项
 if ( SOCKET_ERROR == SetSocketOption(hSocket) ) //设置属性失败
 {
  CloseSocket(hSocket, TRUE);
  return (INVALID_SOCKET);
 }
 return (hSocket);
}


//引入该函数的目的是为了避免NT下对域名解析的CACHE造成的问题
//入口:无
//出口:返回主机IP,或者INADDR_NONE
DWORD WINAPI DNSThread(  LPVOID pParam )
{
 DWORD dwIP = INADDR_NONE;
 PHOSTENT pHost = gethostbyname( (char *)pParam );
 if(pHost == NULL)
  return INADDR_NONE;
 dwIP = inet_addr( inet_ntoa(*(IN_ADDR *)*pHost->h_addr_list) );
 return dwIP;
}

//取得一个指定名字或者IP字串的DWORD IP表示
//入口:主机IP或者域名,是否启动新线程进行查找IP
//出口:返回IP,或者INADDR_NONE
DWORD CSocketModel::GetIP(const char* name,BOOL bFixNtDNS /* = FALSE*/)// Used to Fix NT DNS Problem
{
 //这里是为了将字串IP直接转换成DWORD的形式
 DWORD dwIP = inet_addr(name);
 if( dwIP != INADDR_NONE )
  return dwIP;
 //如果传入的是服务器的域名则进行向下程序
 //如果NT的CATCH有问题,那么进入下面的模块,主要是启动新线程进行域名解析
 if( bFixNtDNS )
 {
  OSVERSIONINFO  osVersion;
  osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if( GetVersionEx(&osVersion) )
  {
   if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
   {
    DWORD dwThreadId = 0;
    HANDLE hThread = CreateThread(NULL,0,DNSThread,(LPVOID)name,0,&dwThreadId);
    if( hThread != NULL)
    {
     //注意这里在等待的不是一个事件,而是线程句柄
     WaitForSingleObject(hThread,INFINITE);
     //注意这里取得线程退出码就是我们需要的IP
     if( GetExitCodeThread(hThread,&dwIP))
      return dwIP;
    }
   }
  }
 }
 //取得主机信息
 PHOSTENT pHost = gethostbyname(name);
 if(pHost == NULL)
  return INADDR_NONE;
  
 dwIP = inet_addr( inet_ntoa(*(IN_ADDR *)*pHost->h_addr_list) );
 return dwIP;
}

// 监听套接字
//入口:套接字,接入的等待队列长度
//出口:SOCKET_ERROR/SOCKET_SUCCESS
int CSocketModel::ListenSocket(SOCKET hSocket, int nConnections)
{
 if(listen(hSocket, nConnections) == SOCKET_ERROR)
 {
  SetLastError( WSAGetLastError() );
  return (SOCKET_ERROR);
 }
 return (SOCKET_SUCCESS);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值