|
I/O 多路复用 |
1 要掌握的数据类型 文件描述符集合fd_set 利用宏FD_ZERO(&rset)初始化 利用宏FD_SET(sockfd,&rset)将sockfd 加入到rset 中 利用宏FD_CLR(sockfd,&rset)将sockfd 从rset 中删除 利用宏FD_ISSET(sockfd,&rset)判定sockfd 是否在rset 集合中 |
2 select 函数 |
函数原型: int select(int maxfd,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout); |
struct timeval{ long tv_sec; long tv_usec; } (假定maxfd 足够大,通常取三个集合中文件描述符(int)最大的加一) select 函数的功能是监视readset,writeset 和execptset 中的文件是否准备就绪, 如果timeout 没有超时的话,且三个集合中没有任何文件就绪,则阻塞。如果有 某些文件就绪,则函数返回。 返回时,还在集合中的文件描述符就是已经准备就绪的文件描述符。 |
3 利用select 函数的程序模型 3.1 简单tcp 服务器的模型 |
1 2 3 4 |
调用socket 函数,int fd= socket(…)产生监听套接字fd 设置地址结构体struct sockaddr_in server 绑定server 和fd 监听 |
while(1) { 5 利用accetpt 函数int confd=accept(fd,….)产生连接套接字confd for(;;) { read(confd,……); } |
} |
字 fd_set rset; 1 调用socket 函数,int fd= socket(…)产生监听套接字fd 2 设置地址结构体struct sockaddr_in server 3 绑定server 和fd 4 监听 while(1) { 将fd 加入文件描述符集合rset; select(maxfd+1,&rset,NULL,NULL,NULL); 下面开始探测哪个文件描述符是可读的 if fd 可读 { //说明fd 可读 int confd=accept(fd,…..,)//调用accept 函数产生连接套接字confd 将confd 加入rset 中 } |
if 连接套接字可读 { 去找哪个连接套接字可读比如xfd 可读 调用read 函数n= read(xfd,buf,BUFSIZE); 如何n 为0 则对方关闭了连接 把xfd 从rset 中删除 } |
} |
3.2 更加细化的模型(加入数组记录每个客户端的信息) 更加细化的select 程序,本质上有一个监听套接字和N 个连接套接字(N 个 客户端),其中N 个连接套接字都是由一个监听套接字产生的。 因此,需要一个数组记录这N 个连接套接字的信息(具体包括套接字的文件描 述符,套接字对应的客户端的地址和端口,套接字的通信消息的缓冲区) 另外,需要考虑的是每次select 返回后,都会修改rset 的值,因此用两个文 件描述符集合,在while 循环的开始,rset 程序的伪代码如下: struct CLIENT { char buf[BUFSIZE]; int fd; struct sockaddr_in addr; } struct CLIENT client[MAX_CLIENT]; fs_set rset,allset |
2 设置地址结构体struct sockaddr_in server 3 绑定server 和fd 4 监听 while(1) { 将fd 加入文件描述符集合allset; Rset=allset select(maxfd+1,&rset,NULL,NULL,NULL); if (fd 在集合rset 中 { |
int confd=accept(fd,…..,)//调用accept 函数产生连接套接字confd 将confd 加入rset 中 |
} |
for(i=0;i<MAX_CLIENT;i++) |
{ |
if(client[i].fd 在rset 中) { 调用read 函数n= read(client[i].fd,client[i].buf,BUFSIZE); if(n==0) {对方关闭了连接, 把client[i].fd 从allset 中删除 并且close(client[i].fd)} 否则打印client[i].buf 中的内容 } } } |
4 文件代码如下: |
#include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <unistd.h> #include <pthread.h> #include <stdio.h> #include <string.h> #include <arpa/inet.h> |
#define PORT 5001 |
#define CLIENT_SIZE 50 #define MAX_CLIENT 100 void printbi8(fd_set a) { int i; char k=*((char*)(&a)); for(i=0;i<8;i++) { if((k&1)==1)printf("1"); else printf("0"); k=k>>1; } printf("\r\n"); |
} struct CLIENT { char buf[BUFSIZE]; int fd ; struct sockaddr_in addr; }; struct CLIENT client[MAX_CLIENT]; int main() { |
int i; for(i=0;i<MAX_CLIENT;i++) client[i].fd=-1; int fd=socket(AF_INET,SOCK_STREAM,0); |
struct sockaddr_in server; struct sockaddr_in cli; inet_aton("0.0.0.0",&server.sin_addr); server.sin_port=htons(PORT); server.sin_family=AF_INET; |
int bret=bind(fd,(struct sockaddr*)&server,sizeof server); if(bret<0){printf("error bind!\r\n");exit(-1);}; |
listen(fd,128); |
fd_set rset,allset; FD_ZERO(&allset); |
int maxfd=fd; while(1) { |
rset=allset; //printbi8(rset); int rs=select(maxfd+1,&rset,NULL,NULL,NULL); //printbi8(rset); if(rs<0){printf("error select!\r\n");exit(-1);}; |
if(FD_ISSET(fd,&rset)) { //监听套接字可读 int addrlen=sizeof(cli); int confd=accept(fd,(struct sockaddr*)&cli,&addrlen); if(confd<0){printf("error accept!\r\n");exit(-1);}; |
//去找到一个可使用的client[x] |
for(i=0;i<MAX_CLIENT;i++) { if(client[i].fd==-1) { client[i].fd=confd; client[i].buf[0]=0; memcpy(&client[i].addr,&cli,sizeof cli); FD_SET(confd,&allset);//加入文件描述符集合, maxfd=confd>maxfd?confd:maxfd; printf("72 hello!\r\n"); break; } |
} if(i==MAX_CLIENT){printf("TOO MANY Clients!\r\n");exit(-1);}; |
if(--rs==0)continue;// 如果只有一个文件是可读的,则不用去查看 client 数组中的文件是否可读了 } |
for(i=0;i<MAX_CLIENT;i++) { if(FD_ISSET(client[i].fd,&rset)) { |
if(k==0) {//客户端退出 printf("client[%d]from%s:%d quit!\r\n",i,inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port)); FD_CLR(client[i].fd,&allset); close(fd); client[i].fd=-1; if(--rs==0)break;//没有可读文件描述符了 continue; } if(k==-1){printf("error in read!\r\n");exit(-1);}; client[i].buf[k]=0; printf("client[%d]from%s:%dsend message:%s\r\n",i,inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port),clie nt[i].buf); if(--rs==0)break; } }//end of for }//end of while } |
转载于:https://blog.51cto.com/wolfword/1240343