博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux I/O多路转接之select函数
阅读量:6679 次
发布时间:2019-06-25

本文共 5451 字,大约阅读时间需要 18 分钟。

I/O多路转接的基本思想:先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。

注意:每一个路的IO操作,尽可能的时间短,不要有while循环。

POSIX.1标准定义了select函数,这可使得我们能够执行I/O多路转接。

#include
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);返回值:准备就绪的描述符数,若超时则返回0, 若出错则返回-1

select的第一个参数nfds的意思是“最大描述符加1”。在三个描述符集中找出最大描述符编号值,然后加1,这就是第一个参数。也可设置FD_SETSIZE,即常量1024,说明最大描述符数。

select的中间三个参数readfds、writefds和exceptfds是指向描述符集的指针。这三个描述符说明了我们关心的可读、可写或处于异常条件的各个描述符。每个描述符集存放在一个fd_set数据类型中,这种数据类型为每一个可能的描述符保持一位。

对fd_set数据类型可以进行的处理是:分配一个这种类型的变量;将这种类型的一个变量赋予同类型的另一个变量;或对这种类型的变量使用以下四个函数中的一个:

#include
int FD_ISSET(int fd, fd_set *set); 返回值:若fd在描述符集中则返回非零值,否则返回0void FD_CLR(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);

以上这些接口可实现为宏函数。调用FD_ZERO将一个指定的fd_set变量的所有位设置为0,即清空列表;调用FD_SET设置一个fd_set变量的指定位,即向表中添加一个文件描述符;调用FD_CLR则将一指定位清除,即从表中,移除一个文件描述符;最后,调用FD_ISSET测试一指定位是否设置,即判断文件描述符是否准备就绪。

声明了一个描述符集后,必须用FD_ZERO清除其所有位,然后设置各个位。

如:

1 fd_set         rset; 2 int               fd; 3  4 FD_ZERO(&rset); 5 FD_SET(fd, &rset); 6 FD_SET(STDIN_FILENO, &rset); 7  8 if(FD_ISSET(fd, &rset)){ 9             ………………10 }

当然,select的这中间三个参数中的一个或全部都可以是空指针,这表示相应状态并不关心。

select的最后一个参数timeout,表示愿意等待的时间:

1 struct timeval{2     long tv_sec;  /*seconds*/3     long tv_usec;/*and  microseconds*/4 };

有三种情况:

(1)timeout == NULL

永远等待。若捕捉到一个信号则中断此等待。当所等待的描述符中的一个已经准备好或捕捉到一个信号则返回。若捕捉到一个信号,则select返回-1,errno设置为EINTR。

(2)timeout->tv_sec == 0 && timeout->tv_usec == 0

完全不等待。测试所有指定的描述符并立即返回。

(3)timeout->tv_sec != 0 || timeout->tv_usec != 0

等待指定的秒数和微秒数。当指定的描述符之一已准备好,或当指定的时间值已经超过时立即返回。若超时时还没有一个描述符准备好,则返回值为0.

对于select有三个可能的返回值

(1)返回值为1表示出错。如:所指定的描述符都没准备好就捕捉到一个信号。

(2)返回值0表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经超时,则发生这种情况。此时所有描述符集皆被清零。

(3)正返回值表示已经准备好的描述符数,该数值是三个描述符集中已准备好的描述符数之和,所以若同一描述符已准备好读和写,那么再返回值中将其记为2.

示例程序:

 server.c

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 13 #define N 128 14 15 16 int main(int argc, const char *argv[]) 17 { 18 struct sockaddr_in serveraddr, clientaddr; 19 int sockfd; 20 int maxfd; 21 fd_set readfds, tmpfds; 22 int i = 0; 23 char buf[N] = { 0}; 24 int connectfd; 25 socklen_t len = sizeof(struct sockaddr); 26 27 if(argc < 3) 28 { 29 fprintf(stderr,"Usage:%s serverip port.", argv[0]); 30 return -1; 31 } 32 33 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 34 { 35 perror("fail to socket"); 36 return -1; 37 } 38 39 serveraddr.sin_family = AF_INET; 40 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); 41 serveraddr.sin_port = htons(atoi(argv[2])); 42 43 if(bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) 44 { 45 perror("fail to bind"); 46 return -1; 47 } 48 49 if(listen(sockfd, 10) < 0) 50 { 51 perror("fail to listen"); 52 return -1; 53 } 54 55 printf("sockfd = %d\n", sockfd); 56 57 maxfd = sockfd; 58 59 FD_ZERO(&readfds); 60 FD_SET(sockfd, &readfds); 61 62 while(1) 63 { 64 tmpfds = readfds; 65 if(select(maxfd+1, &tmpfds, NULL, NULL, NULL) < 0) 66 { 67 perror("fail to select"); 68 return -1; 69 } 70 71 for(i = 0; i < maxfd+1; i++) 72 { 73 if(FD_ISSET(i, &tmpfds)) 74 { 75 if(i == sockfd) // sockfd 76 { 77 if((connectfd = accept(sockfd, (struct sockaddr *)&clientaddr, &len)) < 0) 78 { 79 perror("fail to accept"); 80 return -1; 81 } 82 printf("client:%s %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); 83 84 FD_SET(connectfd, &readfds); 85 maxfd = maxfd > connectfd ? maxfd:connectfd; 86 } 87 else // A B C 88 { 89 if(recv(i, buf, N, 0) < 0) 90 { 91 perror("fail to recv"); 92 return -1; 93 } 94 buf[strlen(buf) -1] = '\0'; 95 printf("client:%s ---> %s %d\n", buf, inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); 96 if(strncmp(buf, "quit", 4) == 0) 97 { 98 FD_CLR(i, &readfds); 99 close(i);100 continue;101 }102 strcat(buf, "++++++++++++");103 if(send(i, buf, N, 0) < 0)104 {105 perror("fail to send");106 return -1;107 }108 } // connectfd109 } // fd_set110 } // for111 112 }//while113 114 115 close(sockfd);116 117 return 0;118 }

 

 client.c

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 11 #define LEN 12812 13 int main(int argc, const char *argv[])14 {15 16 int sockfd;17 struct sockaddr_in serveraddr;18 char buf[LEN];19 20 if(argc != 3)21 {22 fprintf(stderr, "Usage:%s serverip port.\n", argv[0]);23 return -1;24 }25 26 sockfd = socket(AF_INET, SOCK_STREAM, 0);27 if(sockfd < 0)28 {29 perror("fail to socket");30 }31 32 serveraddr.sin_family = AF_INET;33 serveraddr.sin_addr.s_addr = inet_addr(argv[1]);34 serveraddr.sin_port = htons(atoi(argv[2]));35 36 if(connect(sockfd, (struct sockaddr*)&serveraddr,sizeof(struct sockaddr)) < 0)37 {38 perror("connect failed.");39 return -1;40 }41 42 while(1)43 {44 45 printf("input > ");46 fgets(buf, LEN, stdin);47 if(send(sockfd, buf, sizeof(buf), 0) < 0)48 {49 perror("fail to send");50 return -1;51 }52 if(strncmp(buf,"quit", 4) == 0)53 {54 break;55 }56 if(recv(sockfd, buf, sizeof(buf), 0) < 0)57 {58 perror("fail to recv");59 return -1;60 }61 printf("buf:%s\n", buf);62 }63 64 close(sockfd);65 66 67 return 0;68 }

 

转载于:https://www.cnblogs.com/yangziwen0709/p/5020711.html

你可能感兴趣的文章
android判断是否连接网络
查看>>
我的友情链接
查看>>
JNI字段描述符“([Ljava/lang/String;)V”
查看>>
sqlite 打开数据库
查看>>
http://xpleaf.blog.51cto.com/
查看>>
Thrift使用教程(Java版本)
查看>>
我的友情链接
查看>>
通过SSH证书实现Putty免密码登录CentOS
查看>>
Java IO类库之Bits
查看>>
ERROR 1217 (23000): Cannot delete or update a pare
查看>>
oracle 11g RAC搭建 ASM存储
查看>>
函数学习-bytearray()
查看>>
CentOS7安装配置telnet-server
查看>>
GitOSC和GitHub上传项目
查看>>
全局静态变量析构和线程结束先后顺序问题
查看>>
[PYTHON] 核心编程笔记(12.Python模块)
查看>>
windows下MD5-SHA1校验
查看>>
Linux学习记录-2015-08-20--常用命令1
查看>>
Android工程引用另外一个工程的正确/错误方法
查看>>
Testlink使用介绍
查看>>