,,C语言详解select函数的使用

,,C语言详解select函数的使用

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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • 详解c语言中的字符串数组是什么,详解c语言中的字符串数组结构,详解C语言中的字符串数组
  • 表达式求值c++实现,c语言实现表达式求值
  • 看懂c语言基本语法,C语言详解,C语言的基本语法详解
  • 用c语言实现快速排序算法,排序算法设计与实现快速排序C语言,C语言实现快速排序算法实例
  • 深入解析c语言中函数指针的定义与使用方法,深入解析c语言中函数指针的定义与使用情况,深入解析C语言中函数指针的定义与使用
  • 描述E-R图,E-R图举例,关于C语言中E-R图的详解
  • 折半查找法C语言,折半查找算法(算法设计题)
  • 折半查找法C语言,c语言折半法查找数据,C语言实现折半查找法(二分法)
  • 扫雷小游戏c++代码设计,c语言扫雷游戏源代码,C语言实现扫雷小游戏详细代码
  • 怎样统计程序代码行数,C语言统计行数,C#程序员统计自己的代码行数
  • 基于c语言的贪吃蛇游戏程序设计,用c语言编写贪吃蛇游戏程序,C语言实现简单的贪吃蛇游戏
  • 图的两种遍历算法,图的遍历算法代码c语言,Python算法之图的遍历
  • 留言与评论(共有 条评论)
       
    验证码: