我們先來說說最重要的函數(shù)select這個函數(shù),它的原型如下:

int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
select函數(shù)可以執(zhí)行I/O多路轉(zhuǎn)接。傳給select的參數(shù)告訴內(nèi)核:
1.我們所關(guān)心的描述符
2.對于每個描述符我們所關(guān)心的條件(是否想從一個給定的描述符讀,寫,還是異常條件)
3.愿意等待的時間(永遠(yuǎn)等待,等待一個給定的時間或者根本不等待)
從select返回時,內(nèi)核告訴我們:
1.已準(zhǔn)備好的描述符的總數(shù)量
2.對于讀,寫,異常這3個條件中的每一個,哪些描述符已經(jīng)準(zhǔn)備好
有了上面的返回信息,我們就可以調(diào)用相應(yīng)的I/O函數(shù)(read或者write),并且確知該函數(shù)不會被阻塞。
我們再來說說select這個函數(shù)的返回值和相應(yīng)的參數(shù):
正常返回準(zhǔn)備就緒的描述符數(shù)目,若超時,返回0;出錯時返回-1.
參數(shù):
我們先來說最后一個參數(shù),它是一個指向結(jié)構(gòu)體的指針,這個結(jié)構(gòu)體如下:
struct timeval
{
long tv_sec;
long tv_usec;
}
這個參數(shù)指明了愿意等待的時間長度,單位為秒和微妙。當(dāng)它的值為NULL,說明要永遠(yuǎn)等待。如果捕捉到一個信號則中斷無限等待。當(dāng)所指定的描述符的一個已經(jīng)準(zhǔn)備好或捕捉到一個信號則返回。當(dāng)這個結(jié)構(gòu)體的兩個成員的值均不為0時,則是我們設(shè)定的要指定的秒數(shù)和微秒數(shù)。當(dāng)指定的一個描述符之一已經(jīng)準(zhǔn)備好,或者當(dāng)指定的時間已經(jīng)超過時立即返回。在超時后還沒有一個描述符準(zhǔn)備好,返回0。
中間的 readfds,writefds,exceptfds這三個參數(shù)是指向描述符集的指針。這3個描述符集說明了我們關(guān)心的可讀,可寫或處于異常條件的描述符集合。每個描述符集存儲在一個fd_set數(shù)據(jù)類型中。這個數(shù)據(jù)類型為每一個描述符保持一位。在這里我們簡單的認(rèn)為是一個很大的字節(jié)數(shù)組。
第一個參數(shù) nfds 的意思是“大文件描述符編號值加1”。因為描述符從0 開始,所以要在大描述符編號上加1.意思是我們我們在所有描述符集當(dāng)中找出大的一個,然后加1就行了。這里我們也可以將第一個參數(shù)設(shè)置為FD_SETSIZE,它是一個常量值,它指定大描述符數(shù)(一般是1024),一般而言,此值有點大。通過我們指定大的描述符數(shù),內(nèi)核就只需在此范圍內(nèi)尋找打開的位,大大節(jié)省了時間。
我們再來說說fd_set這個數(shù)據(jù)類型:
這種數(shù)據(jù)類型的變量經(jīng)常用到以下函數(shù):
int FD_ISSET(int fd,fd_set *fdset);
void FD_CLR(int fd,fd_set *fdset);
void FD_SET(int fd,fd_set *fdset);
void FD_ZERO(fd_set* fdset);
這些接口可實現(xiàn)為宏或者函數(shù)。調(diào)用FD_ZERO將一個fd_set變量的所有位設(shè)置為0.調(diào)用FD_SET設(shè)置描述符集中的一位。調(diào)用FD_CLR可以清除一位。我們可以調(diào)用FD_ISSET測試描述符集中的一個指定位是否已經(jīng)打開。我們要注意,在我們聲明了一個描述符集之后,必須調(diào)用FD_ZERO函數(shù)將這個描述符集置為0,然后再在其中設(shè)置我們關(guān)心的各個描述符的位。
上面說了那么多,我們來用用這幾個函數(shù)吧,在下面的程序中,我們用剛說的那幾個函數(shù)寫了一個簡單的客戶/服務(wù)器間通信模型,客戶向服務(wù)端發(fā)送數(shù)據(jù),服務(wù)端收到后再回顯給客戶端,我們來看看具體代碼:
server端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define _BACKLOG_ 5
int fds[64];
static void usage(const char* arg)
{
printf("usage:%s [ip][poort]",arg);
}
static int startup(char *ip,int port)
{
assert(ip);
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(1);
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
exit(2);
}
if(listen(sock,_BACKLOG_)<0)
{
perror("listen");
exit(3);
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
usage(argv[0]);
exit(1);
}
int port=atoi(argv[2]);
char *ip=argv[1];
int listen_sock=startup(ip,port);
int done=0;
int new_sock=-1;
struct sockaddr_in client;
socklen_t len=sizeof(client);
int max_fd;
fd_set reads;
fd_set writs;
int i=0;
int fds_num=sizeof(fds)/sizeof(fds[0]);
for( ;i<fds_num;++i)
{
fds[i]=-1;
}
fds[0]=listen_sock;
max_fd=fds[0];
while(!done)
{
FD_ZERO(&reads);
FD_ZERO(&writs);
FD_SET(listen_sock,&reads);
struct timeval timeout={5,0};
for(i=1;i<fds_num;++i)
{
if(fds[i]>0)
{
FD_SET(fds[i],&reads);
if(fds[i]>max_fd)
{
max_fd=fds[i];
}
}
}
switch(select(max_fd+1,&reads,&writs,NULL,&timeout))
{
case 0://超時
printf("select timeout\n");
break;
case -1:
perror("select");
break;
default:
{
i=0;
for(;i<fds_num;++i)
{
if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads))
{//監(jiān)聽套接字就緒
new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_sock<0)
{
perror("accept");
continue;
}
printf("get a new connect...%d\n",new_sock);
for(i=0;i<fds_num;++i)
{
if(fds[i]==-1)
{
fds[i]=new_sock;
break;
}
}
if(i==fds_num)
{
close(new_sock);
}
}//listen sock
else if(fds[i]>0&&FD_ISSET(fds[i],&reads))
{
char buf[1024];
ssize_t _s=read(fds[i],buf,sizeof(buf)-1);
if(_s>0)
{
buf[_s]='\0';
printf("client : %s",buf);
write(fds[i],buf,_s);
}
else if(_s==0)
{
printf("client quit..\n");
close(fds[i]);
fds[i]=-1;
}
else{
}
}//nomal socket
else{
}
}
}
break;
}
}
return 0;
}下面是client端:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
void usage(const char* arg)
{
printf("%s [remote_ip][remote_port\n",arg);
}
int main(int argc,char *argv[])
{
if(argc != 3)
{
usage(argv[0]);
exit(0);
}
int port=atoi(argv[2]);
char *ip=argv[1];
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(1);
}
struct sockaddr_in remote;
remote.sin_family=AF_INET;
remote.sin_port=htons(port);
remote.sin_addr.s_addr=inet_addr(ip);
int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote));
char buf[1024];
while(1)
{
printf("please enter: ");
fflush(stdout);
ssize_t _s=read(0,buf,sizeof(buf)-1);
buf[_s]='\0';
write(sock,buf,sizeof(buf)-1);
_s=read(sock,buf,sizeof(buf)-1); //回顯
if(_s>0)
{
buf[_s]='\0';
printf("server-->client:%s",buf);
}
}
return 0;
}上面的結(jié)果如下:

從上面的結(jié)果我我們可以看到,從client端發(fā)送的數(shù)據(jù),最后被server端hui顯給client端了,達(dá)到了我們的目的。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
網(wǎng)站名稱:select---基于TCP客戶/服務(wù)端編程-創(chuàng)新互聯(lián)
文章出自:http://chinadenli.net/article16/hdjdg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、網(wǎng)站排名、做網(wǎng)站、定制開發(fā)、App設(shè)計、動態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容