生活随笔
收集整理的這篇文章主要介紹了
基于select模型的TCP服务器
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
之前的一篇博文是基于TCP的服務器和客戶機程序,今天在這我要實現(xiàn)一個基于select模型的TCP服務器(僅實現(xiàn)了服務器)。
socket套接字編程提供了很多模型來使服務器高效的接受客戶端的請求,select就是其中之一。
了解select模型我們先來看一下的代碼:
int iResult = recv(s, buffer,1024);
這 是用來接收數(shù)據(jù)的,在默認的阻塞模式下的套接字里,recv會阻塞在那里,直到套接字連接上有數(shù)據(jù)可讀,把數(shù)據(jù)讀到buffer里后recv函數(shù)才會返 回,不然就會一直阻塞在那里。在單線程的程序里出現(xiàn)這種情況會導致主線程(單線程程序里只有一個默認的主線程)被阻塞,這樣整個程序被鎖死在這里,如果永 遠沒數(shù)據(jù)發(fā)送過來,那么程序就會被永遠鎖死。這個問題可以用多線程解決,但是在有多個套接字連接的情況下,這不是一個好的選擇,擴展性很差。Select 模型就是為了解決這個問題而出現(xiàn)的。
select函數(shù):
[cpp] view plain
copy <span?style="font-family:Microsoft?YaHei;font-size:14px;">int?select(int?nfds,?fd_set??*readfds,?fd_set??*writefds,?fd_set?*exceptfds,?const?struct?timeval?*timeout?);</span>??
參數(shù)nfds是需要監(jiān)視的最?的?件描述符值+1;
rdset,wrset,exset分別對應于需要檢測的可讀?件描述符的集合,可寫?件描述符的集 合及異
常?件描述符的集合。
struct timeval結(jié)構(gòu)?于描述?段時間長度,如果在這個時間內(nèi),需要監(jiān)視的描述符沒有事件
發(fā)?則函數(shù)返回,返回值為0。
[cpp] view plain
copy <span?style="font-family:Microsoft?YaHei;font-size:14px;">timeval結(jié)構(gòu)體:??struct??timeval?{??????????long????tv_sec;??????????????????long????tv_usec;???????};</span>??
select返回fd_set中可用的套接字個數(shù)。
下?的宏提供了處理這三種描述詞組的?式:
FD_CLR(inr fd,fd_set* set);?來清除描述詞組set中相關(guān)fd 的位
FD_ISSET(int fd,fd_set *set);?來測試描述詞組set中相關(guān)fd 的位是否為真
FD_SET(int fd,fd_set*set);?來設置描述詞組set中相關(guān)fd的位
FD_ZERO(fd_set *set);?來清除描述詞組set的全部位
函數(shù)返回值:
執(zhí)?成功則返回?件描述詞狀態(tài)已改變的個數(shù)。
如果返回0代表在描述詞狀態(tài)改變前已超過timeout時間,沒有返回;
當有錯誤發(fā)?時則返回-1, 錯誤原因存于errno,此時參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預測。錯誤值可能為:
EBADF ?件描述詞為?效的或該?件已關(guān)閉。
EINTR 此調(diào)?被信號所中斷。
EINVAL 參數(shù)n 為負值。
ENOMEM 核?內(nèi)存不?。
根據(jù)以上的只是前提,我們可以得到select的特點:
select模型的特點:
(1)可監(jiān)控的?件描述符個數(shù)取決與sizeof(fd_set)的值。我這邊服務 器上sizeof(fd_set)=
512,每bit表??個?件描述符,則我服務器上?持的最??件描述符是512*8=4096。據(jù)說
可調(diào),另有說雖 然可調(diào),但調(diào)整上限受于編譯內(nèi)核時的變量值。本?對調(diào)整fd_set的??不
http://www.cppblog.com /CppExplore/archive/2008/03/21/45061.html?太感興趣,參考中的模
型2(1)可以有效突破select可監(jiān)控的?件描述符上 限。
(2)將fd加?select監(jiān)控集的同時,還要再使??個數(shù)據(jù)結(jié)構(gòu)array保存放到select監(jiān)控集
中的fd,?是?于再select 返回后,array作為源數(shù)據(jù)和fd_set進?FD_ISSET判斷。?是select
返回后會把以前加?的但并?事件發(fā)?的fd清空,則每次開始 select前都要重新從array取得fd
逐?加?(FD_ZERO最先),掃描array的同時取得fd最?值maxfd,?于select的第?個 參
數(shù)。
(3)可見select模型必須在select前循環(huán)array(加fd,取maxfd),select返回后循環(huán)array
(FD_ISSET判斷是否有時間發(fā)?)。
以下是select模型的工作過程:
1:用FD_ZERO宏來初始化我們感興趣的fd_set。
也就是select函數(shù)的第二三四個參數(shù)。
2:用FD_SET宏來將套接字句柄分配給相應的fd_set。
如果想要檢查一個套接字是否有數(shù)據(jù)需要接收,可以用FD_SET宏把套接接字句柄加入可讀性檢查隊列中
3:調(diào)用select函數(shù)。
如果該套接字沒有數(shù)據(jù)需要接收,select函數(shù)會把該套接字從可讀性檢查隊列中刪除掉,
4:用FD_ISSET對套接字句柄進行檢查。
如果我們所關(guān)注的那個套接字句柄仍然在開始分配的那個fd_set里,那么說明馬上可以進行相應的IO操 作。比如一個分配給select第一個參數(shù)的套接字句柄在select返回后仍然在select第一個參數(shù)的fd_set里,那么說明當前數(shù)據(jù)已經(jīng)來了, 馬上可以讀取成功而不會被阻塞。
基于select模型的TCP服務器的實現(xiàn):
[cpp] view plain
copy <span?style="font-family:Microsoft?YaHei;font-size:14px;">#include<stdio.h>??#include<string.h>??#include<stdlib.h>??#include<sys/socket.h>??#include<sys/types.h>??#include<sys/select.h>??#include<netinet/in.h>????#define?_MAX_SIZE_?10??int?fd_arr[_MAX_SIZE_];??int?max_fd=0;??static?void?Useage(const?char*?proc)??{??????printf("Useage:%s,[ip][port]");??????exit(1);??}??static?int?add_fd_arr(int?fd)??{????????????int?i=0;??????for(;i<_MAX_SIZE_;++i)??????{??????????if(fd_arr[i]==-1)??????????{??????????????fd_arr[i]=fd;??????????????return?0;??????????}??????}??????return?1;??}??int?select_server(char*?ip,char*?port)??{??????struct?sockaddr_in?ser;??????struct?sockaddr_in?cli;??????fd_set?fds;??????int?fd=socket(AF_INET,SOCK_STREAM,0);??????if(fd<0)??????{??????????perror("socket()");??????????exit(2);??????}??????int?yes=1;??????setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int));????????memset(&ser,'\0',sizeof(ser));??????ser.sin_family=AF_INET;??????ser.sin_port=htons(port);??????ser.sin_addr.s_addr=ip;??????????if(bind(fd,(struct?sockaddr*)&ser,sizeof(ser))<0)??????{??????????perror("bind()");??????????exit(3);??????}??????????????int?i=0;??????for(;i<_MAX_SIZE_;++i)??????{??????????fd_arr[i]=-1;??????}????????add_fd_arr(fd);??????????FD_ZERO(&fds);??????if(listen(fd,5)<0)??????{??????????perror("listen");??????????exit(4);??????}??????????while(1)??????{????????????????????for(i=0;i<_MAX_SIZE_;++i)??????????{??????????????if(fd_arr[i]!=-1)??????????????{??????????????????FD_SET(fd_arr[i],&fds);??????????????????if(fd_arr[i]>max_fd)??????????????????{??????????????????????max_fd=fd_arr[i];??????????????????}??????????????}??????????}??????????struct?timeval?timeout={3,0};??????????switch(select(max_fd+1,&fds,NULL,NULL,&timeout))??????????{??????????????case?-1:??????????????????{??????????????????????perror("select");??????????????????????exit(5);??????????????????????break;??????????????????}??????????????case?0:??????????????????{??????????????????????printf("select?timeout......");??????????????????????break;??????????????????}??????????????default:??????????????????{??????????????????????for(i=0;i<_MAX_SIZE_;++i)??????????????????????{??????????????????????????if(i==0&&fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds))??????????????????????????{??????????????????????????????socklen_t?len=sizeof(cli);??????????????????????????????int?new_fd=accept(fd,(struct?sockaddr*)&cli,&len);??????????????????????????????if(-1!=new_fd)??????????????????????????????{??????????????????????????????????printf("get?a?new?link");??????????????????????????????????if(1==add_fd_arr(new_fd))??????????????????????????????????{??????????????????????????????????????perror("fd_arr?is?full,close?new_fd\n");??????????????????????????????????????close(new_fd);??????????????????????????????????}????????????????????????????????????????????????????????????????}??????????????????????????????continue;??????????????????????????}??????????????????????????if(fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds))??????????????????????????{??????????????????????????????char?buf[1024];??????????????????????????????memset(buf,'\0',sizeof(buf));??????????????????????????????ssize_t?size=recv(fd_arr[i],buf,sizeof(buf)-1,0);??????????????????????????????if(size==0||size==-1)??????????????????????????????{??????????????????????????????????printf("remote?client?close,size?is%d\n",size);??????????????????????????????????int?j=0;??????????????????????????????????for(;j<_MAX_SIZE_;++j)??????????????????????????????????{??????????????????????????????????????if(fd_arr[j]==fd_arr[i])??????????????????????????????????????{??????????????????????????????????????????fd_arr[j]=-1;??????????????????????????????????????????break;??????????????????????????????????????}??????????????????????????????????}??????????????????????????????????close(fd_arr[i]);??????????????????????????????????FD_CLR(fd_arr[i],&fds);??????????????????????????????}else??????????????????????????????{??????????????????????????????????printf("fd:%d,msg:%s",fd_arr[i],buf);??????????????????????????????}??????????????????????????}??????????????????????}??????????????????}??????????????????break;??????????}????????}??}??int?main(int?argc,char*?argv[])??{??????if(argc!=3)??????{??????????Useage(argv[0]);??????}??????select_server(argv[1],argv[2]);??????return?0;??}??</span>?
總結(jié)
以上是生活随笔為你收集整理的基于select模型的TCP服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。