select函数详解及使用案例
1、select函数原型
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
参数解释:
maxfdp——传入参数,集合中所有文件描述符的范围,即最大文件描述符值+1readfds——传入传出参数,select调用时传入要监听的可读文件描述符集合,select返回时传出发生可读事件的文件描述符集合writefds——传入传出参数,select调用时传入要监听的可写文件描述符集合,select返回时传出发生可写事件的文件描述符集合errorfds——传出参数,select返回时传出发生事件(包括可读和可写)中异常事件的文件描述符集合timeout——传入参数,设置select阻塞的时间。若设置为NULL,则select一直阻塞直到有事件发生;若设置为0,则select为非阻塞模式,执行后立即返回;若设置为一个大于0的数,即select的阻塞时间,若阻塞时间内有事件发生就返回,否则时间到了立即返回
fd_set是自定义的一个数据结构,可看作一个集合,存放可读、可写或异常事件的文件描述符。fd_set集合通常有以下四个宏来操作:
void FD_ZERO(fd_set *fdset); //清空fdset中所有文件描述符
void FD_SET(int fd,fd_set *fdset); //添加文件描述符fd到集合fdset中
void FD_CLR(int fd,fd_set *fdset); //将文件描述符fd从集合fdset中去除
int FD_ISSET(int fd,fd_set *fdset); //判断文件描述符fd是否在集合fdset中
select工作原理:传入要监听的文件描述符集合(可读、可写或异常)开始监听,select处于阻塞状态,当有事件发生或设置的等待时间timeout到了就会返回,返回之前自动去除集合中无事件发生的文件描述符,返回时传出有事件发生的文件描述符集合。但select传出的集合并没有告诉用户集合中包括哪几个就绪的文件描述符,需要用户后续进行遍历操作。
2、select优缺点
优点:
(1)select的可移植性较好,可以跨平台;
(2)select可设置的监听时间timeout精度更好,可精确到微秒,而poll为毫秒。
缺点:
(1)select支持的文件描述符数量上限为1024,不能根据用户需求进行更改;
(2)select每次调用时都要将文件描述符集合从用户态拷贝到内核态,开销较大;
(3)select返回的就绪文件描述符集合,需要用户循环遍历所监听的所有文件描述符是否在该集合中,当监听描述符数量很大时效率较低。
3、select使用经典案例
用select函数编写一个简单的高并发服务器,且假设服务器启动时处于无连接状态,满足以下功能:
a)可处理来自一个新客户端的连接请求;
b)监听可读事件,若已连接客户端的已连接描述符发生可读事件,服务器从客户端读取数据并处理;
服务器端代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<ctype.h>#define SERV_PORT 6666int main()
{int i,j,n,maxi;int maxfd,listenfd,connfd,sockfd;int nready,client[FD_SETSIZE-1]; //FD_SETSIZE=1024,定义数组client来储存已连接描述符,最多1023个char buf[BUFSIZ], str;struct sockaddr_in clie_addr,serv_addr;socklen_t clie_addr_len;fd_set allset,readset; //定义监听描述符集合allset和发生事件描述符集合readsetbzero(&serv_addr,sizeof(serv_addr));serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERV_PORT): //端口号,将无符号短整型转换为网络字节顺序serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); //一个主机可能有多个网卡,所以是本机的任意IP地址listenfd=socket(AF_INET,SOCK_STREAM,0); //AF_INET表示使用32位IP地址,SOCK_STREAM表示使用TCP连接bind(listenfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)); //将服务器套接字地址与套接字描述符联系起来listen(listenfd,1024); //设置可监听的连接数量为1024maxfd=listenfd; //初始化最大文件描述符为监听描述符listenfd//初始化client数组,将数组所有元素置为-1int maxi=-1; //数组client储存的文件描述符的个数,初始化为-1for (i=0;i<FD_SETSIZE;i++)client[i]=-1//初始化select监听文件描述符的集合FD_ZERO(&allset); //初始化监听集合FD_SET(listenfd,&allset); //将监听描述符listenfd添加到集合中while(1){readset=allset;nready=select(maxfd+1,&readset,NULL,NULL,NULL); //select只监听可读事件,且为永久阻塞直到有事件发生if (nready<0)perr_exit("select error");//判断listenfd是否发生事件,若发生,则处理新客户端连接请求if (FD_ISSET(listenfd,&readset)){clie_addr_len=sizeof(clie_addr);connfd=accept(listenfd,(struct sockaddr *)&clie_addr,&clie_addr_len);//与请求客户端建立连接printf(“received from %s at port %d\n”,inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,&str,sizeof(str)),ntohs(clie_add.sin_port)); //打印该客户端的IP地址和端口号//将connfd赋值给client数组中第一个为-1的元素位置for (i=0;i<FD_SETSIZE;i++){if (client[i]<0){client[i]=connfd;break;}}//判断select监听的文件描述符的个数是否超过上限if (i == FD_SIZE-1) //减1的原因是要考虑监听描述符listenfd也属于select监控{fputs("too many clients\n",stderr);exit(1);}FD_SET(connfd,&allset); //向监控的文件描述符集合allset中添加新的描述符connfdif (connfd>maxfd)maxfd=connfd; //更新最大文件描述符值//保证maxi永远是client数组中最后一个非-1的元素的位置if(i>maxi)maxi=i;//如果nready=1,即只有一个发生事件的描述符,在此条件下必为listenfd,则返回循环位置,继续调用select监控;否则继续向下执行--nready;if (nready==0)continue;}//找到client数组中发生事件的已连接描述符,并读取、处理数据for (i=0;i<=maxi;i++){sockfd=client[i];if (sockfd<0) //已连接描述符失效,重新开始循环continue; if (FD_ISSET(sockfd,&readset)){n=read(sockfd,buf,sizeof(buf));if (n==0) //当客户端关闭连接,服务端也关闭连接{colse(sockfd);FD_CLR(sockfd,&allset); //解除select对该已连接文件描述符的监控client[i]=-1;}else if (n>0){for (j=0;j<n;j++)buf[j]=toupper(buf[j]);sleep(2);write(sockfd,buf,n);}--nready;if (nready==0)break; //跳出for循环,还在while中}}}close(listenfd);return 0;
}
客户端代码
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>#define SERV_IP "127.0.0.1" //客户端、服务端都在一台主机上,所以直接用本机IP地址
#define SERV_PORT 6666int main()
{int cfd;struct sockaddr_in serv_addr;char buf[BUFSIZ];int n;cfd=socket(AF_INET,SOCK_STREAM,0);memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERV_PORT);inet_pton(AF_INET,SERV_IP,&serv_addr.sin_addr.s_addr); //将点十进制字节串转换为网络字节序connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));while(1){fgets(buf,sizeof(buf),stdin);write(cfd,buf,strlen(buf));n=Read(cfd,buf,sizeof(buf));write(STDOUT_FILENO,buf,n);}close(cfd);return 0;
}
将服务端、客户端生成可执行文件之后,先启动服务器,再启动客户端与服务器建立连接,用户输入字符,服务器将读取到的字符转换为大写,再写回客户端的屏幕上,测试结果如下所示:
服务器会显示客户端的IP地址以及端口号,可发现客户端与服务器来自同一台主机,这是没有问题的
客户端会显示用户输入的字符串以及转为大写后写回的字符串,如图:
注意:数据传输完毕后,一定要客户端先断开连接,避免服务器出现TIME_WAIT状态,从而占用服务器资源。
select函数详解及使用案例相关推荐
- 利用MATLAB求解非线性优化问题---fgoalattain函数详解及应用案例
最近在做方程组的非线性优化问题,用到了fgoalattain函数,总结一下: 意义 解决多目标的非线性优化问题 函数形式 函数表示形式如下: 上式中,weight, goal, b和beq 是向量(组 ...
- Linux I/O复用之select函数详解
http://blog.csdn.net/y396397735/article/details/55004775 select函数的功能和调用顺序 使用select函数时统一监视多个文件描述符的: ...
- select()函数详解
elect()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet().accept().recv()或recvfr ...
- select 函数详解
Unix系统下解释: 头文件: #include <sys/time.h> 函数原型:int select(int maxfdp, fd_set* readfds, fd_set* wr ...
- select函数详解
select函数的功能和调用顺序 使用select函数可以完成非阻塞方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况--读写或是异常. 非阻塞方式:non-block,就是进程或线程执行此 ...
- TCP/IP编程之select函数详解
前述: linux下的I/O复用模型目前很多都已经不用select函数了,而是用epoll,但是为什么还需要了解select编程呢,其实是从两个方面考虑的:一是为了通过select去理解epoll,而 ...
- linux select函数详解
在Linux中,我们可以使用select函数实现I/O端口的复用,传递给 select函数的参数会告诉内核: •我们所关心的文件描述符 •对每个描述符,我们所关心的状态.(我们是要想从一个文件描述符中 ...
- select函数详解及实例分析
Select函数在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect. accept.recv或recvfrom这样的阻塞 ...
- linux下wait函数,Linux wait函数详解
wait和waitpid出现的原因 SIGCHLD --当子进程退出的时候,内核会向父进程SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) --子进程退出时,内核将 ...
最新文章
- JAVA实现查看详情功能_【JavaWeb】111:详情页面的实现
- OpenCV背景扣除Background Subtraction
- 文件服务器冷热数据划分,游戏服务器冷热数据分离方案
- 『後起Android开发02』对SharedPreferences和Toast的简单封装
- HTML自动刷新页面
- php代码 编码转换,php字符编码转换代码
- 微信小程序父子组件之间传值
- jdk重复安装,Error:Registry key ‘Software\JavaSoft\Java Runtime Environment\CurrentVersion(已解决)
- git pull 报错:Exiting because of unfinished merge.
- 了解Nape 2d物理引擎 第一天
- np.add.at和np.negative.at
- Excel拆分字符判断是否有汉字
- 电商时代的逆向思维法则
- 阿里云域名购买与域名解析使用教程
- 密码学归约证明——DH密钥交换协议在窃听者存在的情况下的安全性
- 游戏修改器制作教程一:键盘鼠标模拟
- 游戏感:虚拟感觉的游戏设计师指南——第十五章 超级马里奥64
- 图像比对-感知哈希算法
- cmatrix秀一波骚操作
- 51单片机静态动态数码管显示