多路复用I/O:一个执行体监视多个文件描述符对象的状态是否改变,一旦改变通知其他执行体来实现。
基本思想:
1、 先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或者多个已准备好进行I/O时函数才返回
2、 函数返回时告诉进程那个描述符已准备就绪,可以进行I/O操作。
Select(int n,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout);
注意两点:
1、 select函数返回之前会将集合中状态未改变的fd清除
2、 返回值为状态改变的文件描述符对象的个数。
3、 Select阻塞时内部会产生一个线程盯着描述符对象的状态改变。
参数:n:maxfd,所有监控的文件描述符中值最大的加1;
Read_fds:是否可读的文件描述符集
Write_fds:是否可写的文件描述符集
Except_fds:出错的文件描述符集
Timeout:如果设置为NULL,则会一直阻塞,直到文件描述符的状态改变
Struct timeval
{
Long tv_sec;
Long tv_usec;
} ;
文件描述符的几个宏:
FD_ZERO(fd_set *fdset):清空文件描述符集
FD_SET(int fd,fd_set *fdset):将fd加入到fd_set集中
FD_ISSET(int fd,fd_set*fdset):判断fd是否在fdset中
FD_CLR(fd,fd_set *fdset);将fd从fdset集中清除
Select函数使用范例(功能:本例使用的是tcp,select监听标准输入是否准备好字符串可读然后发送和socketfd是否可读,然后接受,实现以简单的qq通信)
客户端: #include<stdio.h> #include<pthread.h> #include<netinet/in.h> #include<stdlib.h> #include<string.h> #include<strings.h> #include<sys/types.h> #include<sys/socket.h> int main() { int socketfd,ret; fd_set fd; FD_ZERO(&fd); FD_SET(0,&fd); socketfd = socket(PF_INET,SOCK_STREAM,0); FD_SET(socketfd,&fd); ret = select(socketfd+1,&fd,NULL,NULL,NULL); struct sockaddr_in saddr; memset(&saddr,0,sizeof(saddr)); saddr.sin_family = PF_INET; saddr.sin_port = htons(6000); saddr.sin_addr.s_addr = inet_addr("192.168.1.46"); if(connect(socketfd,(struct sockaddr *)&saddr,sizeof(struct sockaddr))<0) perror("connect() error! "); char buf[20],buf1[20]; while(1) { bzero(buf,20); bzero(buf1,20); FD_SET(0,&fd); FD_SET(socketfd,&fd); ret = select(socketfd+1,&fd,NULL,NULL,NULL); if(FD_ISSET(socketfd,&fd)) { if(recv(socketfd,buf1,sizeof(buf1),0)<0) perror("recv() error! "); printf("%s",buf1); continue; } if(FD_ISSET(0,&fd)) { // printf("please input: "); fgets(buf,20,stdin); if(send(socketfd,buf,strlen(buf),0)<0) perror("send() error! "); continue; } } } 服务器端: #include<stdio.h> #include<netinet/in.h> #include<stdlib.h> #include<string.h> #include <sys/types.h> #include <sys/socket.h> #include<pthread.h> #include<strings.h> int main() { int fds[12]; int maxfd = -1; int socketfd = socket(PF_INET,SOCK_STREAM,0); if(socketfd<maxfd) maxfd = socketfd; struct sockaddr_in saddr; memset(&saddr,0,sizeof(saddr)); saddr.sin_family = PF_INET; saddr.sin_port = htons(6000); saddr.sin_addr.s_addr = inet_addr("192.168.193.2"); int newsocketfd; int n = 1; setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(int)); if(bind(socketfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr_in))<0) perror("bind() error! "); if(listen(socketfd,5)<0) perror("listen() error! "); struct sockaddr_in caddr; int s = sizeof(struct sockaddr); newsocketfd=accept(socketfd,(struct sockaddr*)&caddr,&s); fd_set fd; FD_ZERO(&fd); int ret; char buf[20],buf1[20]; int i; int rsize; printf("hello "); while(1) { bzero(buf,20); bzero(buf1,20); // printf("hello "); FD_SET(0,&fd); // printf("hello "); FD_SET(newsocketfd,&fd); // printf("hello "); select(newsocketfd+1,&fd,NULL,NULL,NULL); if(FD_ISSET(newsocketfd,&fd)) { // printf("hello "); if(recv(newsocketfd,buf1,sizeof(buf1),0)<0) perror("recv() error! "); printf("%s",buf1); // printf("hello "); continue; } if(FD_ISSET(0,&fd)) { fgets(buf,20,stdin); if(send(newsocketfd,buf,strlen(buf),0)<0) perror("send() error! "); continue; } } close(socketfd); close(newsocketfd); }