常用服务器架构实现

常用服务器架构实现(多线程/多进程 & select模型 & 完成端口/EPOLL)

  RT,常用的服务器架构原理如上面所示三种,虽然出现的时间不同,但即使到今天也仍然是各有侧重的,没有绝对的优劣之分

1)多线程/多进程

适用于多个连接间共享数据较少的情况,对于每一个客户端连接都会fork一个子进程,APACH是典型的代表。源码如下:

struct stClientInfo 
{
 int connfd;
 std::string s_ip;
};

DWORD CServer::_ReadThreadFunc(LPVOID lpParam)
{
 stClientInfo *pClientInfo = (stClientInfo *)lpParam;
 if (NULL == pClientInfo)
  return 1;

 static const int BUF_SIZE = 512;
 char buf[BUF_SIZE + 1];

 while (true)
 {
  if (recv(pClientInfo->connfd, buf, BUF_SIZE, 0) <= 0) // socket断开
   break;
  buf[BUF_SIZE] = 0;
  printf("%s 套接字%d:%s\n", pClientInfo->s_ip.c_str(), pClientInfo->connfd, buf);
 }

 printf("客户端断开! ip: %s  套接字: %d\n", pClientInfo->s_ip.c_str(), pClientInfo->connfd);
 closesocket(pClientInfo->connfd);
 
 delete pClientInfo;
 return 0;
}

bool CServer::Run()
{
 SOCKADDR_IN saServer;
 int nRet = 0;

 // 创建套接字
 m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 if (INVALID_SOCKET == m_sockfd)
 {
  _ErrorMsg(__FUNCTION__, "create socket failed");
  return false;
 }

 // 初始化
 memset(&saServer, 0, sizeof(saServer));
 saServer.sin_family = AF_INET;
 saServer.sin_port = htons(m_nPort);
 saServer.sin_addr.s_addr = htonl(INADDR_ANY); // 远端为任意IP

 // 绑定到端口
 nRet = bind(m_sockfd, (sockaddr *)&saServer, sizeof(saServer));
 if (-1 == nRet) // bind失败
 {
  _ErrorMsg(__FUNCTION__, "bind faild");
  return false;
 }

 // 开始监听
 nRet = listen(m_sockfd, SOMAXCONN);
 if (-1 == nRet) //
 {
  _ErrorMsg(__FUNCTION__, "listen failed");
  return false;
 }
 printf("Server start to listen port %d!\n", m_nPort);

 while(true)
 {
  int nAddrSize = sizeof(saServer);
  int connfd = accept(m_sockfd, (SOCKADDR *)&saServer, &nAddrSize);
  SOCKADDR_IN saClient;
  getpeername(connfd, (SOCKADDR *)&saClient, &nAddrSize);
  printf("客户端连入! ip: %s 套接字: %d\n", inet_ntoa(saClient.sin_addr), connfd);
  stClientInfo *pClientInfo = new stClientInfo();
  pClientInfo->connfd = connfd;
  pClientInfo->s_ip = inet_ntoa(*(struct in_addr *)&saClient.sin_addr);
  ::CreateThread(NULL, 0, _ReadThreadFunc, pClientInfo, 0, NULL);
 }

 return true;
}

2)select模型

非常之经典的模型,适用于连接数不是很多的情况,默认只支持64个客户端,修改后可支持到1024,但理想情况是要到数百个。优点是可以跨平台,假如代码需要同时用在windows与linux上,且连接数不大,那么select有时会是不错的选择。

注,标准的select模型其实有一定的改进空间,主要在于FD_SET、FD_ISSET等几个函数,稍后会提到。

源码:

bool CServer::RunSelectMode()
{
 SOCKADDR_IN saServer;
 int nRet = 0;

 // 创建套接字
 m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 if (INVALID_SOCKET == m_sockfd)
 {
  _ErrorMsg(__FUNCTION__, "create socket failed");
  return false;
 }

 // 初始化
 memset(&saServer, 0, sizeof(saServer));
 saServer.sin_family = AF_INET;
 saServer.sin_port = htons(m_nPort);
 saServer.sin_addr.s_addr = htonl(INADDR_ANY); // 远端为任意IP

 // 绑定到端口
 nRet = bind(m_sockfd, (sockaddr *)&saServer, sizeof(saServer));
 if (-1 == nRet) // bind失败
 {
  _ErrorMsg(__FUNCTION__, "bind faild");
  return false;
 }

 // 开始监听
 nRet = listen(m_sockfd, SOMAXCONN);
 if (-1 == nRet) //
 {
  _ErrorMsg(__FUNCTION__, "listen failed");
  return false;
 }
 printf("Server start to listen port %d!\n", m_nPort);

 // 初始化套接字集合
 fd_set fdSocket;
 FD_ZERO(&fdSocket);
 FD_SET(m_sockfd, &fdSocket);
 
 while(true)
 {
  fd_set fdRead = fdSocket;
  int nRet = ::select(m_sockfd, &fdRead, NULL, NULL, NULL); // 找出当前有读信号的socket集合
  if (nRet <= 0)
  {
   _ErrorMsg(__FUNCTION__, "select failed");
   break;
  }
  for (int nIndex = 0; nIndex < fdSocket.fd_count; ++nIndex) // 遍历所有socket
  {
   if (FD_ISSET(fdSocket.fd_array[nIndex], &fdRead)) // 如果该socket属于fdRead,即该socket有读信号
   {
    if (fdSocket.fd_array[nIndex] == m_sockfd) // 监听socket收到读信号,即有新连接连入
    {
     if (fdSocket.fd_count < FD_SETSIZE) // 连接数未超过最大连接数
     {
      SOCKADDR_IN addrRemote;
      int nLen = sizeof(addrRemote);
      SOCKET sNew = accept(m_sockfd, (SOCKADDR *)&addrRemote, &nLen); // 接受新连接
      FD_SET(sNew, &fdSocket); // 添加到socket集合中
      printf("客户端连入! ip: %s 套接字: %d\n", inet_ntoa(addrRemote.sin_addr), sNew);
     }
     else
     {
      printf("too much connections!\n");
      continue;
     }
    }
    else // 其他socket收到读信号,即有客户端消息传入
    {
     static const int BUF_SIZE = 512;
     static char pszBuf[BUF_SIZE];
     int nRecv = recv(fdSocket.fd_array[nIndex], pszBuf, BUF_SIZE - 1, 0);

     if (nRecv > 0) // 可读,有新数据到来
     {
      pszBuf[nRecv] = 0;
      printf("%d: %s\n", fdSocket.fd_array[nIndex], pszBuf);
     }
     else   // 连接断开
     {
      printf("客户端断开! 套接字: %d\n", fdSocket.fd_array[nIndex]);
      closesocket(fdSocket.fd_array[nIndex]); // 关闭对应socket
      FD_CLR(fdSocket.fd_array[nIndex], &fdSocket); // 从socket集合中删除该socket
     }
    }
   }
  }
 }

 return true;
}

3)完成端口  很简陋,未考虑数据包排序及异步投递接收请求

 

 

CIOCPServer::CIOCPServer(int nPort, int nMaxAccepts)
{
 m_nPort = nPort;
 m_nMaxAccepts = nMaxAccepts;

 m_hListenThread = NULL;
 m_hWorkThread = NULL;

 m_sock = INVALID_SOCKET;
 m_hCompletion = NULL;
}

CIOCPServer::~CIOCPServer(void)
{
}

bool CIOCPServer::Run()
{
 // 创建重叠套接字
 m_sock = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
 if (INVALID_SOCKET == m_sock)
 {
  printf("failed to create socket! errno = %d\n", WSAGetLastError());
  return false;
 }

 // 绑定本地端口
 SOCKADDR_IN sa;
 sa.sin_family = AF_INET;
 sa.sin_addr.s_addr = htonl(INADDR_ANY);
 sa.sin_port = htons(m_nPort);

 if (::bind(m_sock, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR)
 {
  printf("failed to bind! errno = %d\n", WSAGetLastError());
  return false;
 }

 // 监听端口
 if (::listen(m_sock, m_nMaxAccepts) == SOCKET_ERROR)
 {
  printf("failed to listen! errno = %d\n", WSAGetLastError());
  return false;
 }

 // 创建完成端口对象
 m_hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);

 // 将监听套接字关联到完成端口对象,completion key 为0
 ::CreateIoCompletionPort((HANDLE)m_sock, m_hCompletion, 0, 0);

 m_hListenThread = ::CreateThread(NULL, 0, __ListenThreadFunc, this, 0, NULL);
 m_hWorkThread = ::CreateThread(NULL, 0, __WorkThreadFunc, this, 0, NULL);

 return true;
}

DWORD CALLBACK CIOCPServer::__ListenThreadFunc(LPVOID lpParam)
{
 CIOCPServer *pObj = (CIOCPServer *)lpParam;
 if (NULL == pObj)
  return 1;

 int nRet = pObj->__ListenFunc();
 
 return nRet;
}

DWORD CALLBACK CIOCPServer::__WorkThreadFunc(LPVOID lpParam)
{
 CIOCPServer *pObj = (CIOCPServer *)lpParam;
 if (NULL == pObj)
  return 1;

 int nRet = pObj->__WorkFunc();

 return nRet;
}

DWORD CIOCPServer::__ListenFunc()
{
 SOCKADDR_IN sa;
 sa.sin_family = AF_INET;
 sa.sin_port = htons(m_nPort);
 sa.sin_addr.s_addr = htonl(INADDR_ANY);

 printf("server start to listen!\n");
 while (true)
 {
  int nSize = sizeof(sa);
  SOCKET s = accept(m_sock, (SOCKADDR *)&sa, &nSize); // 接收新客户端连接
  CIOCPContext *pContext = new CIOCPContext();
  CIOCPBuffer *pBuffer = new CIOCPBuffer();

  pContext->s = s;
  pBuffer->s = s;
  pBuffer->buff = new char[BUF_SIZE];
  pBuffer->nOperation = CIOCPBuffer::OP_READ;
  pBuffer->nLen = BUF_SIZE;
  
  ::CreateIoCompletionPort((HANDLE)s, m_hCompletion, (DWORD)pContext, 0);  // 将新连接关联到完成端口对象

  _PostRecv(pContext, pBuffer);
  printf("connection established! socket: 0x%x\n", s);
 }

 return 0;
}

DWORD CIOCPServer::__WorkFunc()
{
 DWORD dwKey;
 DWORD dwTrans;
 LPOVERLAPPED lpol;

 while (true)
 {
  bool bRet = ::GetQueuedCompletionStatus(m_hCompletion, 
   &dwTrans, // 完成字节数
   &dwKey, // 完成端口的key
   &lpol, // 获取重叠结构 
   WSA_INFINITE);

  if (-1 == dwTrans) // 用户通知退出,?
  {
   printf("WorkThread exit!\n");
   ::ExitThread(0);
  }

  CIOCPBuffer *pBuffer = CONTAINING_RECORD(lpol, CIOCPBuffer, ol);
  int nErr = NO_ERROR;

  CIOCPContext *pContext = (CIOCPContext *)dwKey;
  
  //printf("socket 0x%x: has new signal!\n", pContext->s);
  // 开始处理 
  switch (pBuffer->nOperation)
  {
  case CIOCPBuffer::OP_ACCEPT: // 接收
   {
    if (0 == dwTrans)
    {
     if (pBuffer->s != INVALID_SOCKET)
     {
      ::closesocket(pBuffer->s);
      pBuffer->s = INVALID_SOCKET;
     }
    }
    else
    {

    }

   }
   break;
  case CIOCPBuffer::OP_READ: // 读取
   {
    if (0 == dwTrans) // 对方关闭套接字
    {
     printf("socket 0x%x: disconnect!\n", pContext->s);
     delete[] pBuffer->buff;
     delete pBuffer;
     delete pContext;
    }
    else // 正常读取
    {
     pBuffer->nLen = BUF_SIZE; // 字节数
     printf("socket 0x%x: %s\n", pBuffer->s, pBuffer->buff);
     _PostRecv(pContext, pBuffer);
    }
   }
   break;
  case CIOCPBuffer::OP_WRITE: // 写入
   {

   }
   break;
  }
 }

 return 0;
}

bool CIOCPServer::_PostRecv(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
 WSABUF buf;

 buf.buf = pBuffer->buff;
 buf.len = pBuffer->nLen;

 DWORD dwBytes;
 DWORD dwFlags = 0;
 if (NO_ERROR !=::WSARecv(pContext->s, &buf, 1, &dwBytes, &dwFlags, &pBuffer->ol, NULL))
 {
  int nErrno = ::WSAGetLastError();
  if (nErrno != WSA_IO_PENDING)
  {
   printf("socket 0x%x: _PostRecv error! errno = %d\n", pContext->s, nErrno);
   return FALSE;
  }
 }

 return TRUE;
}

你可能感兴趣的:(服务器架构)