现在的位置: 首页 > 综合 > 正文

多路复用I/O支持多Client的实现及效率讨论

2013年05月21日 ⁄ 综合 ⁄ 共 3448字 ⁄ 字号 评论关闭
1. 引言

多路复用I/O模型(select)是UNIX/LINUX用得的最多的一种I/O模型,在Windows下也可做为一种同步I/O使用。本文给出该I/O模型处理多Client的简单(在主线程中)实现。

2. 关于select

select I/O模型是一种异步I/O模型,在单线程中Linux/WinNT默认支持64个客户端套接字。这种I/O模型主要涉及以下几个函数及宏:
int select(…)、FD_ZERO、FD_SET、FD_ISSET以及FD_SETSIZE。

3. 用select开发一个Server

3.1 只支持单个Client
    // 相关初始化处理, 创建监听套接字
    listen(listensock,  5);
    clientsock  =  accept(listensock,  NULL,  NULL);
    for  (; ;)
    {
             FD_ZERO(&readfds);  
             FD_SET(clientsock, &readfds);
             nResult = select(
                     0,         // Windows中这个参数忽略,Linux中在此处为1
                     readfds,    // 可读套接字集合
                     ……
              )
             if(nResult  = =  SOCKET_ERROR)
                    return –1;

             // 判断cliensock是否处于待读状态
             if  (FD_ISSET(clientsock,  &readfds))
            {
                              // 相关处理
            }

    }

其实Winsock中的WSAEventSelect模型是与之类似的。
3.2  在单线程中支持63个Client
   SOCKET clientsockarray[FD_SETSIZE – 1];   // FD_SETSIZE is 64 
  // 相关初始化处理, 创建监听套接字
  listen(listensock, 5);
  // 初始化套接字数组
 InitSock(clientsockarray);
  FD_ZERO(&readfds);
  FD_SET(listensock,  &readfds);
  for  (; ;)
 {
 nRet  = select(0,  &readfds,  NULL,  NULL,  NULL);
// 判断监听套接字是否可读
 if  (FD_ISSET(listensock,  &readfds))
 {
         clientsock = accept(listensock,  NULL,  NULL);
          // 将客户套接字放到套接字数组中
          if   (!InsertSock(clientsockarray, clientsock))
          {
                   printf("客户端超过了63个,此次连接被拒绝./n");
                   closesocket(clientsock);
                   continue;
           }   
  }
  // 逐个处理处于待决状态的套接字
  for  (nIndex  =  0;  nIndex  <  FD_SETSIZE  -  1;  nIndex++)
 {
           if   (FD_ISSET(clientsockarray[nIndex],  &readfds))
           {
                     nRet  =  recv(clientsockarray[nIndex],  buff,  sizeof(buff),  0);
                     if  (nRet  = =  0  ||  nRet  = =  SOCKET_ERROR)
                     {
                            closesocket(clientsockarray[nIndex]);
                            clientsockarray[nIndex] = INVALID_SOCKET;
                            continue;       // 继续处理套接字句柄数组中的其它套接字
                     }
                     // 将接受到的数据进行处理,此处只将其输出
                     printf("%s/n", buff);
              }
       }
       // 初始化套接字集合
       FD_ZERO(&readfds);
       FD_SET(listensock, &readfds);
       // 将所有有效的套接字句柄加入到套接字句柄数组中
       for (nIndex = 0; nIndex < FD_SETSIZE - 1; nIndex++)
       {
              if (clientsockarray[nIndex] != INVALID_SOCKET)
              FD_SET(clientsockarray[nIndex], &readfds);
       }
}
BOOL InsertSock(SOCKET* pSock,  SOCKET sock)
{
         for(int  nIndex  =  0;  nIndex  <  FD_SETSIZE – 1;  nIndex++)
         {
                  if(pSock[nIndex]  = =  INVALID_SOCKET)
                  {
                          pSock[nIndex] = sock;
                          break;
                  }
          }
          if   (nIndex = = FD_SETSIZE – 1)
                 return FALSE;
          return TRUE;
 }

       上面只是给简要的代码,有的辅助函数也没有给出。用select支持多Client是比较方便的,在一个线程中可支持63个;可以采用多线程支持更大数量的Client。

4.  效率的讨论

4.1  对套接字数组扫描的效率问题

    在上面的程序中,存在多处对套接字句柄的扫描处理,这个肯定会影响效率。不知道各位朋友是怎么处理这个问题的。

4.2 对客户端实时响应问题

上面的程序处理待决的套接字的时候,是逐个处理的,如果响应某个Client的时间长到一定程度的话,肯定会影响对其它客户端的响应。我的解决方法是当这个套接字处于可读的待决状态的话,产生一个子线程去处理------接收数据和处理数据。这样主线程继续自己的工作,其它Client可以得及时的响应;当然当有大量的Client请求时,对线程的控制会成为一个新问题。

在UNIX/LINUX下做一个支持大量Client的Server的话,本人还是最先选择select这种I/O模型,这是因为我还不知道LINUX还有哪些更好的I/O模型。WinNT的话,还有CompletionPort和Overlapped,特别对于有大数据量传送,同时只有少量的Client时,Overlapped可以发挥相当大的作用。各位朋友请给出使用select的好方法。
 

抱歉!评论已关闭.