天天看點

基于select模型的TCP伺服器

socket套接字程式設計提供了很多模型來使伺服器高效的接受用戶端的請求,select就是其中之一。

了解select模型我們先來看一下的代碼:

int iResult = recv(s, buffer,1024);

這 是用來接收資料的,在預設的阻塞模式下的套接字裡,recv會阻塞在那裡,直到套接字連接配接上有資料可讀,把資料讀到buffer裡後recv函數才會返 回,不然就會一直阻塞在那裡。在單線程的程式裡出現這種情況會導緻主線程(單線程程式裡隻有一個預設的主線程)被阻塞,這樣整個程式被鎖死在這裡,如果永 遠沒資料發送過來,那麼程式就會被永遠鎖死。這個問題可以用多線程解決,但是在有多個套接字連接配接的情況下,這不是一個好的選擇,擴充性很差。Select 模型就是為了解決這個問題而出現的。

select函數:

[cpp]

​​ view plain​​

​​ copy​​

  1. <span style="font-family:Microsoft YaHei;font-size:14px;">int select(int nfds, fd_set  *readfds, fd_set  *writefds, fd_set *exceptfds, const struct

參數nfds是需要監視的最⼤的⽂件描述符值+1;

rdset,wrset,exset分别對應于需要檢測的可讀⽂件描述符的集合,可寫⽂件描述符的集 合及異

常⽂件描述符的集合。

struct timeval結構⽤于描述⼀段時間長度,如果在這個時間内,需要監視的描述符沒有事件

發⽣則函數傳回,傳回值為0。

[cpp]

​​ view plain​​

​​ copy​​

  1. <span style="font-family:Microsoft YaHei;font-size:14px;">timeval結構體:  
  2. struct
  3. long    tv_sec;        //秒
  4. long    tv_usec;     //毫秒
  5. };</span>  

select傳回fd_set中可用的套接字個數。

下⾯的宏提供了處理這三種描述詞組的⽅式:

FD_CLR(inr fd,fd_set* set);⽤來清除描述詞組set中相關fd 的位

FD_ISSET(int fd,fd_set *set);⽤來測試描述詞組set中相關fd 的位是否為真

FD_SET(int fd,fd_set*set);⽤來設定描述詞組set中相關fd的位

FD_ZERO(fd_set *set);⽤來清除描述詞組set的全部位

函數傳回值:

執⾏成功則傳回⽂件描述詞狀态已改變的個數。

如果傳回0代表在描述詞狀态改變前已超過timeout時間,沒有傳回;

當有錯誤發⽣時則傳回-1, 錯誤原因存于errno,此時參數readfds,writefds,exceptfds和timeout的值變成不可預測。錯誤值可能為:

EBADF ⽂件描述詞為⽆效的或該⽂件已關閉。

EINTR 此調⽤被信号所中斷。

EINVAL 參數n 為負值。

ENOMEM 核⼼記憶體不⾜。

根據以上的隻是前提,我們可以得到select的特點:

select模型的特點:

  (1)可監控的⽂件描述符個數取決與sizeof(fd_set)的值。我這邊服務 器上sizeof(fd_set)=

512,每bit表⽰⼀個⽂件描述符,則我伺服器上⽀持的最⼤⽂件描述符是512*8=4096。據說

可調,另有說雖 然可調,但調整上限受于編譯核心時的變量值。本⼈對調整fd_set的⼤⼩不

​​http://www.cppblog.com /CppExplore/archive/2008/03/21/45061.html​​ 太感興趣,參考中的模

型2(1)可以有效突破select可監控的⽂件描述符上 限。

  (2)将fd加⼊select監控集的同時,還要再使⽤⼀個資料結構array儲存放到select監控集

中的fd,⼀是⽤于再select 傳回後,array作為源資料和fd_set進⾏FD_ISSET判斷。⼆是select

傳回後會把以前加⼊的但并⽆事件發⽣的fd清空,則每次開始 select前都要重新從array取得fd

逐⼀加⼊(FD_ZERO最先),掃描array的同時取得fd最⼤值maxfd,⽤于select的第⼀個 參

數。

  (3)可見select模型必須在select前循環array(加fd,取maxfd),select傳回後循環array

(FD_ISSET判斷是否有時間發⽣)。

以下是select模型的工作過程:

1:用FD_ZERO宏來初始化我們感興趣的fd_set。

也就是select函數的第二三四個參數。

2:用FD_SET宏來将套接字句柄配置設定給相應的fd_set。

如果想要檢查一個套接字是否有資料需要接收,可以用FD_SET宏把套接接字句柄加入可讀性檢查隊列中

3:調用select函數。

如果該套接字沒有資料需要接收,select函數會把該套接字從可讀性檢查隊列中删除掉,

4:用FD_ISSET對套接字句柄進行檢查。

如果我們所關注的那個套接字句柄仍然在開始配置設定的那個fd_set裡,那麼說明馬上可以進行相應的IO操 作。比如一個配置設定給select第一個參數的套接字句柄在select傳回後仍然在select第一個參數的fd_set裡,那麼說明目前資料已經來了, 馬上可以讀取成功而不會被阻塞。

基于select模型的TCP伺服器的實作:

  1. <span style="font-family:Microsoft YaHei;font-size:14px;">#include<stdio.h>  
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<sys/socket.h>
  5. #include<sys/types.h>
  6. #include<sys/select.h>
  7. #include<netinet/in.h>
  8. #define _MAX_SIZE_ 10
  9. int
  10. int
  11. static void Useage(const char* proc)  
  12. {  
  13. "Useage:%s,[ip][port]");  
  14.     exit(1);  
  15. }  
  16. static int add_fd_arr(int
  17. {  
  18. //fd add to fd_arr
  19. int
  20. for(;i<_MAX_SIZE_;++i)  
  21.     {  
  22. if(fd_arr[i]==-1)  
  23.         {  
  24.             fd_arr[i]=fd;  
  25. return
  26.         }  
  27.     }  
  28. return
  29. }  
  30. int select_server(char* ip,char* port)  
  31. {  
  32. struct
  33. struct
  34.     fd_set fds;  
  35. int
  36. if(fd<0)  
  37.     {  
  38. "socket()");  
  39.         exit(2);  
  40.     }  
  41. int
  42. sizeof(int));  
  43. '\0',sizeof(ser));  
  44.     ser.sin_family=AF_INET;  
  45.     ser.sin_port=htons(port);  
  46.     ser.sin_addr.s_addr=ip;  
  47. if(bind(fd,(struct sockaddr*)&ser,sizeof(ser))<0)  
  48.     {  
  49. "bind()");  
  50.         exit(3);  
  51.     }  
  52. //init fd_arr
  53. int
  54. for(;i<_MAX_SIZE_;++i)  
  55.     {  
  56.         fd_arr[i]=-1;  
  57.     }  
  58.     add_fd_arr(fd);  
  59.     FD_ZERO(&fds);  
  60. if(listen(fd,5)<0)  
  61.     {  
  62. "listen");  
  63.         exit(4);  
  64.     }  
  65. while(1)  
  66.     {  
  67. //reset fd_arr
  68. for(i=0;i<_MAX_SIZE_;++i)  
  69.         {  
  70. if(fd_arr[i]!=-1)  
  71.             {  
  72.                 FD_SET(fd_arr[i],&fds);  
  73. if(fd_arr[i]>max_fd)  
  74.                 {  
  75.                     max_fd=fd_arr[i];  
  76.                 }  
  77.             }  
  78.         }  
  79. struct
  80. switch(select(max_fd+1,&fds,NULL,NULL,&timeout))  
  81.         {  
  82. case
  83.                 {  
  84. "select");  
  85.                     exit(5);  
  86. break;  
  87.                 }  
  88. case
  89.                 {  
  90. "select timeout......");  
  91. break;  
  92.                 }  
  93. default:  
  94.                 {  
  95. for(i=0;i<_MAX_SIZE_;++i)  
  96.                     {  
  97. if(i==0&&fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds))  
  98.                         {  
  99. sizeof(cli);  
  100. int new_fd=accept(fd,(struct
  101. if(-1!=new_fd)  
  102.                             {  
  103. "get a new link");  
  104. if(1==add_fd_arr(new_fd))  
  105.                                 {  
  106. "fd_arr is full,close new_fd\n");  
  107.                                     close(new_fd);  
  108.                                 }  
  109.                             }  
  110. continue;  
  111.                         }  
  112. if(fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds))  
  113.                         {  
  114. char
  115. '\0',sizeof(buf));  
  116. sizeof(buf)-1,0);  
  117. if(size==0||size==-1)  
  118.                             {  
  119. "remote client close,size is%d\n",size);  
  120. int
  121. for(;j<_MAX_SIZE_;++j)  
  122.                                 {  
  123. if(fd_arr[j]==fd_arr[i])  
  124.                                     {  
  125.                                         fd_arr[j]=-1;  
  126. break;  
  127.                                     }  
  128.                                 }  
  129.                                 close(fd_arr[i]);  
  130.                                 FD_CLR(fd_arr[i],&fds);  
  131. else
  132.                             {  
  133. "fd:%d,msg:%s",fd_arr[i],buf);  
  134.                             }  
  135.                         }  
  136.                     }  
  137.                 }  
  138. break;  
  139.         }  
  140.     }  
  141. }  
  142. int main(int argc,char* argv[])  
  143. {  
  144. if(argc!=3)  
  145.     {  
  146.         Useage(argv[0]);  
  147.     }  
  148.     select_server(argv[1],argv[2]);  
  149. return
  150. }  
  151. </span>