WinSock I/O系列1:多路複用I/O支持多Client的實現及效率討論

 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的好方法。


來源:http://waiter94.blog.163.com/blog/static/5277376920103413919364/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章