IO多路复用—select

int select(int nfds,  fd_set *rdset,  fd_set *wrset,  fd_set *exset,  struct timeval *timeout);

selsect存在的缺点:

  如果没有I/O事件产生,我们的程序就会阻塞在select处。但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,但却并不知道是那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流(文件描述符),找出能读出数据,或者写入数据的流,对他们进行操作。使用select,有O(n)的无差别轮询复杂度,同时处理的流越多,每一次无差别轮询时间就越长。

注:每次调用select后,除发生变化的文件描述符对应的位外,其他的所有位都初始化为0。所以每次调用函数之前,要用一个fd_set变量复制下来,用复制的fd_set变量作为参数。

1、参数nfds是需要监视的最大的文件描述符值+1;

2、rdset, wrset, exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。

  FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位

  FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真

  FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位

  FD_ZERO(fd_set *set);用来清除描述词组set的全部位。

  其中的fd,就是监控的描述符。

3、struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

结构体具体内容如下。

struct timeval {

   long tv_sec; 秒

   long tv_usec; 毫秒
}

  如果tv_sec和tv_usec都是0,那么就是超时时间为0,select就会立刻返回了。

  如果timeout这里是个NULL,那么超时就未被启用,会一直阻塞在监视文件描述符的地方。

返回值:出错返回-1,超时返回0,有事件发生返回大于0的数。

如果timeout超时之前有文件描述符可用,则返回可用的数量,这时候的timeout则会依然计数,因此如果想要每次都超时一定的时间那么在slelect返回>0的值之后要重新装填timeout的值一次。

利用IO多路复用select,建立多客户端连接,客户端向服务器发送需要计算的数和符号,服务器返回计算结果。代码如下:

  //select_server.c  
1
#include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/socket.h> 5 #include <arpa/inet.h> 6 7 #define backlog 5 8 #define OPSZ 4 9 #define BUF_SIZE 1024 10 11 void error_handling(char *message); 12 int caculate(int opnd_cnt, int opinfo[], char operator); 13 14 int main() 15 { 16 int result; 17 int listen_sock, new_sock; 18 struct sockaddr_in serv_addr, clnt_addr; 19 20 int on = 1, len=sizeof(on); 21 socklen_t clnt_addr_len = sizeof(clnt_addr); 22 23 listen_sock = socket(PF_INET, SOCK_STREAM, 0); 24 if(listen_sock == -1) 25 error_handling("listen_sock() error"); 26 27 //设置端口重用 28 if(setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, len) < 0) 29 error_handling("setsockopt() error"); 30 31 /*设置服务器地址和端口*/ 32 memset(&serv_addr, 0, sizeof(serv_addr)); 33 serv_addr.sin_family = AF_INET; 34 serv_addr.sin_port = htons(24266); 35 serv_addr.sin_addr.s_addr = inet_addr("192.168.1.4"); 36 37 //绑定本地地址信息 38 if(bind(listen_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) 39 error_handling("bind() error"); 40 41 if(listen(listen_sock, backlog) == -1) 42 error_handling("listen() error"); 43 44 int i, j, fd_max; 45 46 /*读、写、异常事件*/ 47 fd_set read_fds, cpy_reads; 48 fd_set write_fds, cpy_writes; 49 fd_set except_fds, cpy_excepts; 50 51 fd_max = listen_sock; 52 FD_ZERO(&read_fds); 53 FD_ZERO(&write_fds); 54 FD_ZERO(&except_fds); 55 56 /*将listen_sock设置为reads,因为是在等待请求响应,相当于当前的读取操作。*/ 57 FD_SET(listen_sock, &read_fds); 58 while(1){ 59 struct timeval timeout={10, 0}; //设置select超时,10秒轮询,要非阻塞就置0 60 61 cpy_reads = read_fds; //每次调用select后,除发生变化的文件描述符对应的位外,其他的所有位都初始化为0。所以要复制下来。 62 switch(select(fd_max+1, &cpy_reads, &write_fds, &except_fds, &timeout)){ //监视文件描述符集合 63 case 0: //没有事件发生,超时 64 printf("timeout.....\n"); 65 break; 66 case -1: 67 perror("select"); 68 break; 69 default: 70 for(i = 0; i < fd_max+1; i++){ 71 if(FD_ISSET(i, &cpy_reads)){ 72 /*1、确认是否是监听事件,是的话就需要accept*/ 73 if(i == listen_sock){ 74 new_sock = accept(listen_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_len); 75 if(new_sock == -1) 76 error_handling("accept() error"); 77 //在fd_set变量reads中注册新连接的套接字文件描述符 78 FD_SET(new_sock, &read_fds); 79 if(fd_max < new_sock) 80 fd_max = new_sock; 81 printf("get a new connet...%d\n",new_sock); 82 } 83 /*2、正常事件,非监听事件,代表是新建立的new_sock*/ 84 else{ 85 char opinfo[BUF_SIZE]; //接收消息的buffer 86 char operator; //算术符号( + - * / ) 87 int opnd_cnt; //被计算的数字个数 88 int number[opnd_cnt]; //数字数组 89 90 ssize_t s = read(i, opinfo, sizeof(opinfo)-1); 91 if(s == 0){ //客户端发送'\n',断开连接 92 printf("client[%d] quit...\n", i); 93 FD_CLR(i, &read_fds); 94 close(i); 95 } 96 else if(s > 0){ 97 int m; 98 opnd_cnt = (int)opinfo[0]; 99 100 for(m = 0; m < opnd_cnt; m++){ 101 number[m] = (int)opinfo[m*OPSZ+1]; 102 } 103 operator = opinfo[m*OPSZ+1]; 104 result = caculate(opnd_cnt, number, operator); 105 write(i, &result, 4); //响应客户端,有可能失败 106 } 107 } 108 } 109 else if(i > 0 && FD_ISSET(i, &write_fds)){ 110 111 } 112 } 113 break; 114 } 115 } 116 close(listen_sock); 117 return 0; 118 } 119 120 int caculate(int opnd_cnt, int number[], char operator) 121 { 122 int value = number[0]; 123 switch(operator) 124 { 125 case '+': 126 while(--opnd_cnt) 127 value += number[opnd_cnt]; 128 break; 129 case '-': 130 while(--opnd_cnt) 131 value -= number[opnd_cnt]; 132 break; 133 case '*': 134 while(--opnd_cnt) 135 value *= number[opnd_cnt]; 136 break; 137 case '/': 138 while(--opnd_cnt) 139 value /= number[opnd_cnt]; 140 break; 141 } 142 printf("vaule:%d\n", value); 143 return value; 144 } 145 146 void error_handling(char *message) 147 { 148 printf("%s\n", message); 149 exit(1); 150 }
 //select_client.c
1
#include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/socket.h> 5 #include <arpa/inet.h> 6 7 #define backlog 5 8 #define OPSZ 4 9 #define BUF_SIZE 1024 10 11 void error_handling(char *message); 12 13 int main() 14 { 15 int sock, i; 16 struct sockaddr_in serv_addr; 17 18 int count, result; 19 char operator, command[256]; 20 char opmsg[BUF_SIZE]; 21 22 sock = socket(PF_INET, SOCK_STREAM, 0); 23 if(sock == -1) 24 error_handling("serv_sock() error"); 25 26 memset(&serv_addr, 0, sizeof(serv_addr)); 27 serv_addr.sin_family = AF_INET; 28 serv_addr.sin_port = htons(24266); 29 serv_addr.sin_addr.s_addr = inet_addr("172.25.25.30"); 30 31 if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) 32 error_handling("connect() error"); 33 printf("Connect..........\n"); 34 35 printf("Continue or quit?(c/q)"); 36 while(1){ 37 scanf("%s", command); 38 if(strncmp(command, "c", 1) == 0){ 39 printf("Operand count:"); 40 scanf("%d", &count); 41 opmsg[0] = (char)count; 42 43 for(i = 0; i < count; i++) 44 { 45 printf("Operand %d:", i+1); 46 scanf("%d", (int *)&opmsg[i*OPSZ+1]); 47 } 48 49 printf("Operator:"); 50 scanf(" %c", (char *)&opmsg[i*OPSZ+1]); 51 52 if(write(sock, opmsg, OPSZ*count+2) == -1) 53 error_handling("write() error"); 54 55 read(sock, &result, 4); 56 57 printf("result:%d\n", result); 58 printf("Continue or quit?(c/q)"); 59 } 60 else if(strncmp(command, "q", 1) == 0){ 61 write(sock, '\n', 1); 62 break; 63 } 64 else{ 65 printf("Continue or quit?(c/q)"); 66 } 67 } 68 close(sock); 69 return 0; 70 } 71 72 void error_handling(char *message) 73 { 74 printf("%s\n", message); 75 exit(1); 76 }

 

posted on 2017-11-28 14:35  Itsad  阅读(590)  评论(0编辑  收藏  举报

导航