C语言中的Select函数一般使用connect、accept、recv或recvfrom等函数,程序阻塞,在socket上接收到数据之前程序无法继续运行。但是,使用选择功能可以实现非阻塞模式的程序。
目录
选择API介绍了编译选择代码和运行选择和轮询的缺点
select
select API介绍
主旨:
首先,要构建一个文件描述符列表,将需要监控的文件描述符添加到列表中。调用一个系统函数,监听列表中的文件描述符,直到这些描述符中的一个或多个执行I/O操作,函数才会返回。
A.这个函数阻塞了。
B.函数检测文件描述符的操作由内核完成。
返回时,它将告诉进程有多少描述符来执行I/O操作。
//sizeof(fd_set)=128字节1024位,每个标志位对应一个文件描述符。
#包含系统/时间. h
#包含sys/types.h
#包括unistd.h
#包含系统/选择. h
int select(int nfds,fd_set *readfds,fd_set *writefds,
fd_set *exceptfds,struct time val * time out);
-参数:
-nfds:受委托内核检测到的最大文件描述符的值为1。
-readfds:要检测的文件描述符读取的集合(发送数据),委托内核检测哪个文件描述符读取的属性。
-通常会检测到读取操作。
-对应的是对方发来的数据,因为读取是被动接收数据,检测到的是读取缓冲区。
-是传入传出参数(内核检测文件描述符标志,检测后返回;)
检测过程:文件描述符处于用户状态,1表示需要检测文件描述符,0表示不需要检测;在内核中处理时:只检测文件描述符为1的文件描述符。如果数据发生变化,则设置为1,如果没有变化,则设置为0,然后返回到用户状态。
-writefds:要检测的文件描述符写入的集合,以及委托内核检测哪个文件描述符写入的属性。
-委托内核检查写缓冲区是否还能写数据(如果不满意可以写)
要检测哪个文件描述符,将标志位置设置为1;
当缓冲区已满时,将相应的文件描述符标志位置设置为0,如果有备用数据要写入,则将其设置为1。
-exceptfds:检测异常的文件描述符的集合。
-超时:设置的超时。
结构时间间隔{
长tv _ sec/*秒*/
long tv _ usec/*微秒*/
};
-NULL:永久阻止,直到检测到文件描述符中的更改。
-tv_sec=0 tv_usec=0,无阻塞
-tv_sec 0 tv_usec 0,阻塞相应的时间
-返回值
-1:失败
-0:超时在选择函数中设置。当超时时间到了,它没有被检测到,并返回0。
-0(n):检测到的集合中有N个文件描述符已更改。
//将参数文件描述符fd对应的标志位设置为0
void FD_CLR(int fd,FD _ set * set);
//判断fd对应的标志位是0还是1。返回值:fd,0,0,1,1对应的标志位的值。
int FD_ISSET(int fd,FD _ set * set);
//将参数文件描述符fd对应的标志位设置为1
void FD_SET(int fd,FD _ SET * SET);
//fd_set有1024位,全部初始化为0。
void FD _ ZERO(FD _ set * set);
首先创建一组文件描述符来检测读取。
fd_set读取;
将3、4、100、101这四个文件描述符设置为1,表示需要检测这些文件描述符;
接下来,调用选择函数:
select(101 1,reads,NULL,NULL,NULL);//第一个参数,要检测的文件描述符1-(1因为从0开始,所以可以遍历到101)
将fd_set从内核态复制到用户态(由内核为我们检测),假设A和B发送数据;
A和B对应的文件描述符是3和4;
结果:3和4有数据,设置为1;00和101没有数据,因此将它们的1设置为0。
修改后,将fd_set从内核态复制到用户态;
用户可以遍历这个集合,找到哪个文件描述符是1,即3和4,这意味着有数据。
select 代码
//客户端
#包含标准视频
#包括arpa/inet.h
#包含标准库
#包括unistd.h
#包含字符串。h
int main() {
//创建(电源)插座
int fd=socket(PF_INET,SOCK_STREAM,0);
if(fd==-1) {
perror(’插座');
return-1;
}
结构sockaddr _ in seraddr
inet_pton(AF_INET,' 127.0.0.1 ',seraddr。sin _ addr。s _ addr);
seraddr.sin _ family=AF _ INET
塞拉德。sin _ port=htons(9999);
//连接服务器
int ret=connect(fd,(struct sockaddr *)seraddr,sizeof(seraddr));
if(ret==-1){
perror(’连接');
return-1;
}
int num=0;
while(1) {
char send buf[1024]={ 0 };
sprintf(sendBuf,发送数据“%d”,数量);
write(fd,sendBuf,strlen(send buf)1);
//接收
int len=read(fd,sendBuf,sizeof(send buf));
if(len==-1) {
perror(’读');
return-1;
}else if(len 0) {
printf('read buf=%s\n ',send buf);
}否则{
printf('服务器已经断开连接.\ n’);
打破;
}
//sleep(1);
美国LEEP(1000);
}
关闭(FD);
返回0;
}
//服务端
#包含标准视频
#包括arpa/inet.h
#包括unistd.h
#包含标准库
#包含字符串。h
#包含系统/选择。h
int main() {
//创建(电源)插座
int lfd=socket(PF_INET,SOCK_STREAM,0);
结构sockaddr _ in saddr
萨德勒。sin _ port=htons(9999);
saddr.sin _ family=AF _ INET
萨德勒。sin _ addr。s _ addr=in addr _ ANY
//绑定
bind(lfd,(struct sockaddr *)saddr,sizeof(saddr));
//监听
听(lfd,8);
//创建一个fd_set的集合,存放的是需要检测的文件描述符
fd_set rdset,tmp//fd_set底层可以表示1024个文件描述符
FD _ ZERO(rd集);//初始化
FD_SET(lfd,rd SET);//添加需要监听的文件描述符
int maxfd=lfd//定义最大文件描述符,作为参数传入挑选函数中
while(1) {
tmp=rdset//rdset这个不能变,因为内核再检测时,如果没有数据,就会将其变为0,因此,我们需要复制一份。
//调用挑选系统函数,让内核帮检测哪些文件描述符有数据
int ret=select(maxfd 1,tmp,NULL,NULL,NULL);
if(ret==-1) {
perror(' select ');
退出(-1);
} else if(ret==0) {//这里不可能为0,因为设置了永久阻塞空,直到检测到文件描述符有数据变化
继续;
} else if(ret 0) {//ret只会返回文件描述符发生变化的个数,不知道具体哪个发生了变化,需要遍历查找
//说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
if(FD_ISSET(lfd,tmp)) {//lfd为监听文件描述符
//表示有新的客户端连接进来了
cliaddr中的结构sockaddr _ in
int len=sizeof(cliaddr);
int cfd=accept(lfd,(struct sockaddr *)cliaddr,len);
//将新的文件描述符加入到集合中,下一次挑选检测时,需要检测这些通信的文件描述符有没有数据
FD_SET(cfd,rd SET);
//更新最大的文件描述符
maxfd=maxfd cfd?maxfd:CFD;
}
//检测剩余文件描述符有没有数据变化,从lfd 1开始即可
for(int I=lfd 1;i=maxfdi ) {
if(FD_ISSET(i,tmp)) {
//说明这个文件描述符对应的客户端发来了数据
char buf[1024]={ 0 };
int len=read(i,buf,sizeof(buf));
if(len==-1) {
perror(’读');
退出(-1);
} else if(len==0) {//说明客户端断开连接
printf('客户端已关闭.\ n’);
关闭(一);//关闭文件描述符
FD_CLR(i,rd set);//FD _ SET中不监控这个文件描述符。
} else if(len 0) {
printf('read buf=%s\n ',buf);
write(i,buf,strlen(buf)1);
}
}
}
}
}
关闭(lfd);
返回0;
}
编译运行
打开另一个客户端:
服务器可以看到一个新的客户端进来了:
即使在新客户中:
select和poll缺点
关于C语言选择函数的使用,本文就讲到这里。有关C语言选择函数的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望你以后能支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。