1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
| select实现的流程: { while(1) { sockets = select(); for(socket in sockets) { if(can_read(socket)) { read(socket, buffer); process(buffer); } } } } select的使用:
#include <sys/time.h> #include <sys/select.h> int select(int nfds, fd_set *rdfds, fd_set *wtfds, fd_set *exfds, struct timeval *timeout) 参数说明: nfds:select中监视的文件句柄数,一般设为要监视的文件中的最大文件号加一。该参数让select变得更有效率,因为此时内核就不用去检查大于这个值的文件描述符是否属于这些文件描述符集合。文件描述符集合有一个最大容量限制,由常量FD_SETSIZE来决定,一般最大位1024个描述符 返回值 rdfds:检测输入是否就绪的文件描述符集合 wtfds:检测输出是否就绪的文件描述符集合 exfds:检测异常情况是否发生的文件描述符集合 (1、信包模式下伪终端主设备上从设备状态发生改变;2、流式套接字接收到了外带数据) struct timeval { time_t tv_sec; 秒 suseconds_t tv_usec 微秒 } 超时时间:NULL为阻塞; 什么时候返回:timeout为NULL时,阻塞,等到有fd准备好了才返回,不会NULL时,则超时时间内有fd准备好了返回或者超时后返回; 数据类型fd_set以位掩码的形式来实现,通过四个宏来完成: FD_CLR(inr fd,fd_set* set);将描述符fd从fdset所指向的集合中移除 FD_SET(int fd,fd_set*set);将描述符fd添加到fdset所指向的集合中 FD_ZERO(fd_set *set); 将fdset指向的集合初始化位空 FD_ISSET(int fd,fd_set *set);测试描述词组set中相关fd 的位是否为真 eg: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define MYPORT 1234 #define BACKLOG 5 #define BUF_SIZE 200 int fd_A[BACKLOG]; int conn_amount; void showclient() { int i; printf("client amount: %d\n", conn_amount); for (i = 0; i < BACKLOG; i++) { printf("[%d]:%d ", i, fd_A[i]); } printf("\n\n"); } int main(void) { int sock_fd, new_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr; socklen_t sin_size; int yes = 1; char buf[BUF_SIZE]; int ret; int i; if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(MYPORT); server_addr.sin_addr.s_addr = INADDR_ANY; memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero)); if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(1); } if (listen(sock_fd, BACKLOG) == -1) { perror("listen"); exit(1); } printf("listen port %d\n", MYPORT);
fd_set fdsr; int maxsock; struct timeval tv;
conn_amount = 0; sin_size = sizeof(client_addr); maxsock = sock_fd; while (1) { FD_ZERO(&fdsr); FD_SET(sock_fd, &fdsr);
tv.tv_sec = 30; tv.tv_usec = 0;
for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { FD_SET(fd_A[i], &fdsr); } }
ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv); if (ret < 0) { perror("select"); break; } else if (ret == 0) { printf("timeout\n"); continue; }
for (i = 0; i < conn_amount; i++) { if (FD_ISSET(fd_A[i], &fdsr)) { ret = recv(fd_A[i], buf, sizeof(buf), 0); if (ret <= 0) { printf("client[%d] close\n", i); close(fd_A[i]); FD_CLR(fd_A[i], &fdsr); fd_A[i] = 0; } else { if (ret < BUF_SIZE) memset(&buf[ret], '\0', 1); printf("client[%d] send:%s\n", i, buf); } } }
if (FD_ISSET(sock_fd, &fdsr)) { new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd <= 0) { perror("accept"); continue; }
if (conn_amount < BACKLOG) { fd_A[conn_amount++] = new_fd; printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (new_fd > maxsock) maxsock = new_fd; } else { printf("max connections arrive, exit\n"); send(new_fd, "bye", 4, 0); close(new_fd); break; } } showclient(); }
for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { close(fd_A[i]); } }
exit(0); }
|