生活随笔
收集整理的這篇文章主要介紹了
基于select模型的TCP服务器
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
之前的一篇博文是基于TCP的服務(wù)器和客戶機(jī)程序,今天在這我要實(shí)現(xiàn)一個(gè)基于select模型的TCP服務(wù)器(僅實(shí)現(xiàn)了服務(wù)器)。
socket套接字編程提供了很多模型來使服務(wù)器高效的接受客戶端的請(qǐng)求,select就是其中之一。
了解select模型我們先來看一下的代碼:
int iResult = recv(s, buffer,1024);
這 是用來接收數(shù)據(jù)的,在默認(rèn)的阻塞模式下的套接字里,recv會(huì)阻塞在那里,直到套接字連接上有數(shù)據(jù)可讀,把數(shù)據(jù)讀到buffer里后recv函數(shù)才會(huì)返 回,不然就會(huì)一直阻塞在那里。在單線程的程序里出現(xiàn)這種情況會(huì)導(dǎo)致主線程(單線程程序里只有一個(gè)默認(rèn)的主線程)被阻塞,這樣整個(gè)程序被鎖死在這里,如果永 遠(yuǎn)沒數(shù)據(jù)發(fā)送過來,那么程序就會(huì)被永遠(yuǎn)鎖死。這個(gè)問題可以用多線程解決,但是在有多個(gè)套接字連接的情況下,這不是一個(gè)好的選擇,擴(kuò)展性很差。Select 模型就是為了解決這個(gè)問題而出現(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分別對(duì)應(yīng)于需要檢測(cè)的可讀?件描述符的集合,可寫?件描述符的集 合及異
常?件描述符的集合。
struct timeval結(jié)構(gòu)?于描述?段時(shí)間長(zhǎng)度,如果在這個(gè)時(shí)間內(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中可用的套接字個(gè)數(shù)。
下?的宏提供了處理這三種描述詞組的?式:
FD_CLR(inr fd,fd_set* set);?來清除描述詞組set中相關(guān)fd 的位
FD_ISSET(int fd,fd_set *set);?來測(cè)試描述詞組set中相關(guān)fd 的位是否為真
FD_SET(int fd,fd_set*set);?來設(shè)置描述詞組set中相關(guān)fd的位
FD_ZERO(fd_set *set);?來清除描述詞組set的全部位
函數(shù)返回值:
執(zhí)?成功則返回?件描述詞狀態(tài)已改變的個(gè)數(shù)。
如果返回0代表在描述詞狀態(tài)改變前已超過timeout時(shí)間,沒有返回;
當(dāng)有錯(cuò)誤發(fā)?時(shí)則返回-1, 錯(cuò)誤原因存于errno,此時(shí)參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預(yù)測(cè)。錯(cuò)誤值可能為:
EBADF ?件描述詞為?效的或該?件已關(guān)閉。
EINTR 此調(diào)?被信號(hào)所中斷。
EINVAL 參數(shù)n 為負(fù)值。
ENOMEM 核?內(nèi)存不?。
根據(jù)以上的只是前提,我們可以得到select的特點(diǎn):
select模型的特點(diǎn):
(1)可監(jiān)控的?件描述符個(gè)數(shù)取決與sizeof(fd_set)的值。我這邊服務(wù) 器上sizeof(fd_set)=
512,每bit表??個(gè)?件描述符,則我服務(wù)器上?持的最??件描述符是512*8=4096。據(jù)說
可調(diào),另有說雖 然可調(diào),但調(diào)整上限受于編譯內(nèi)核時(shí)的變量值。本?對(duì)調(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í),還要再使??個(gè)數(shù)據(jù)結(jié)構(gòu)array保存放到select監(jiān)控集
中的fd,?是?于再select 返回后,array作為源數(shù)據(jù)和fd_set進(jìn)?FD_ISSET判斷。?是select
返回后會(huì)把以前加?的但并?事件發(fā)?的fd清空,則每次開始 select前都要重新從array取得fd
逐?加?(FD_ZERO最先),掃描array的同時(shí)取得fd最?值maxfd,?于select的第?個(gè) 參
數(shù)。
(3)可見select模型必須在select前循環(huán)array(加fd,取maxfd),select返回后循環(huán)array
(FD_ISSET判斷是否有時(shí)間發(fā)?)。
以下是select模型的工作過程:
1:用FD_ZERO宏來初始化我們感興趣的fd_set。
也就是select函數(shù)的第二三四個(gè)參數(shù)。
2:用FD_SET宏來將套接字句柄分配給相應(yīng)的fd_set。
如果想要檢查一個(gè)套接字是否有數(shù)據(jù)需要接收,可以用FD_SET宏把套接接字句柄加入可讀性檢查隊(duì)列中
3:調(diào)用select函數(shù)。
如果該套接字沒有數(shù)據(jù)需要接收,select函數(shù)會(huì)把該套接字從可讀性檢查隊(duì)列中刪除掉,
4:用FD_ISSET對(duì)套接字句柄進(jìn)行檢查。
如果我們所關(guān)注的那個(gè)套接字句柄仍然在開始分配的那個(gè)fd_set里,那么說明馬上可以進(jìn)行相應(yīng)的IO操 作。比如一個(gè)分配給select第一個(gè)參數(shù)的套接字句柄在select返回后仍然在select第一個(gè)參數(shù)的fd_set里,那么說明當(dāng)前數(shù)據(jù)已經(jīng)來了, 馬上可以讀取成功而不會(huì)被阻塞。
基于select模型的TCP服務(wù)器的實(shí)現(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服务器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。