select
select的目的让一个线程或进程可以监听多个事件
使用select不会让进程组塞在read,write或者accept上,但是进程会阻塞在select上
函数原型:int select(int
nfds, fd_set*
readfds, fd_set*
writefds, fd_set*
exceptds, const struct timespec*
timeout)
参数解释及返回值
int nfds:三个位图中包含的最大的文件描述符+1
fd_set*:readfds,writefds,exceptfds三个分别描述需要监听读事件,写事件以及异常事件的集合,都是传入传出参数,传入的时候表示希望select帮助我们舰艇的文件描述符的集合,传出的时候表示触发了事件的文件描述符的集合;如果没有需要舰艇的读事件,写事件,或者异常事件,对应的位图传NULL
timeout:表示的超时时间,这个参数也是一个传入传出参数,如果传10,十秒钟之内没有事件触发,则返回0;如果传>0的值,表示的是超时时间,如果传=NULL,select会阻塞到直到有监听的事件发生为止;timeout传0这时候表示非阻塞,只会查看当前是否有监听的事件触发
select返回值:失败返回-1,成功返回触发事件的个数
注意:文件描述符的读写事件,读事件触发:调用read读这个文件描述符read不会阻塞;写事件触发:调用write写这个文件描述符write不会阻塞;
FD_ZERO(fd_set* set);
FD_SET(int,fd,fd_set* set);
FD_CLR(int fd,fd_set* set);
FD_ISSET(int fd,fd_set* set);
select实现多路io复用的代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFSIZE 1024
#define PORT 8000
int main()
{
int lfd, cfd;
char buf[BUFSIZE];
socklen_t addrlen;
lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in ser_addr, cli_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(PORT);
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(lfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(lfd,128);
socklen_t len = sizeof(cli_addr);
//已经有了一个文件描述符需要监听lfd
int nfds;
fd_set rset, aset;
FD_ZERO(&aset);
FD_SET(lfd, &aset);
nfds = lfd;
int i = 0;
while(1)
{
rset = aset;//不写这行代码可能使返回的文件描述符没有了需要监听但是没触发的
int ret = select(nfds+1, &rset, NULL, NULL, NULL);
if(ret == -1)
{
perror("select error");
exit(1);
}
printf("select 返回\n");
if(FD_ISSET(lfd, &rset))
{
int cfd = accept(lfd, (struct sockaddr*)&cli_addr, &len);
printf("cfd = %d\n", cfd);
FD_SET(cfd, &aset);
if(nfds < cfd)
{
nfds = cfd;
}
}
for(i = lfd + 1; i < nfds + 1; i++)
{
if(FD_ISSET(i, &rset))
{
int rr = read(i, buf, BUFSIZE);
if(rr < 0)
{
perror("read errro");
exit(1);
}
else if(rr == 0)
{
FD_CLR(i, &aset);
printf("客户端断开连接cfd = %d\n", i);
close(i);
}
else
{
write(1, buf, rr);
write(i, buf, rr);
}
}
}
}
return 0;
}
扩展:
为什么用位图,因为占用空间小
select优缺点、性能
缺点:1.最多同时监听1024个文件描述符
2.select会造成大量的用户空间到内核空间的数据拷贝,每次调用select都会把一个更新的rset传入select,让select去监听,所以会有大量的用户空间到内核空间的数据拷贝
3.轮询监听机制,在客户端活跃用户量少的时候性能比较低,一直在循环监听,我们查网页的时候浏览的时候,与服务器进行数据交互没有浏览时间长,但是未进行数据交互,select却一直轮询监听,会浪费资源
4.select适合比较少的用户量,因为有事件触发,select会进入内核,无法处理与此同时触发的事件
优点:跨平台
为什么要选择多路io:为了能够实现
更高并发量的服务器,且省更多的资源,位图大小为1024,所以select最多同时能监听1024个文件描述符,位图就是一个long类型的数组,通过修改可以更改监听文件描述符的多少