select这个系统调用,是一种多路复用IO方案,可以同时对多个文件描述符进行监控,从而知道哪些文件描述符可读,可写或者出错,不过select方法是阻塞的,可以设定超时时间。 select使用的步骤如下:
客户端
当然select也有其局限性。当fd_set中的文件描述符较少,或者大都数文件描述符都比较活跃的时候,select的效率还是不错的。Mac系统中已经定义了fd_set 最大可以容纳的文件描述符的个数为1024
每一次select 调用的时候,都涉及到user space和kernel space的内存拷贝,且会对fd_set中的所有文件描述符进行遍历,如果所有的文件描述符均不满足,且没有超时,则当前进程便开始睡眠,直到超时或者有文件描述符状态发生变化。当文件描述符数量较大的时候,将耗费大量的CPU时间。所以后来有新的方案出现了,如windows2000引入的IOCP,Linux Kernel 2.6中成熟的epoll,FreeBSD4.x引入的kqueue。
- 1.创建一个fd_set变量(fd_set实为包含了一个整数数组的结构体),用来存放所有的待检查的文件描述符
- 2.清空fd_set变量,并将需要检查的所有文件描述符加入fd_set
- 3.调用select。若返回-1,则说明出错;返回0,则说明超时,返回正数,则为发生状态变化的文件描述符的个数
- 4.若select返回大于0,则依次查看哪些文件描述符变的可读,并对它们进行处理
- 5.返回步骤2,开始新一轮的检测
#include#include #include #include #include #include #include #define BACKLOG 5 //完成三次握手但没有accept的队列的长度 #define CONCURRENT_MAX 8 //应用层同时可以处理的连接 #define SERVER_PORT 11332 #define BUFFER_SIZE 1024 #define QUIT_CMD ".quit" int client_fds[CONCURRENT_MAX]; int main (int argc, const char * argv[]) { char input_msg[BUFFER_SIZE]; char recv_msg[BUFFER_SIZE]; //本地地址 struct sockaddr_in server_addr; server_addr.sin_len = sizeof(struct sockaddr_in); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server_addr.sin_zero),8); //创建socket int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_sock_fd == -1) { perror("socket error"); return 1; } //绑定socket int bind_result = bind(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (bind_result == -1) { perror("bind error"); return 1; } //listen if (listen(server_sock_fd, BACKLOG) == -1) { perror("listen error"); return 1; } //fd_set fd_set server_fd_set; int max_fd = -1; struct timeval tv; tv.tv_sec = 20; tv.tv_usec = 0; while (1) { FD_ZERO(&server_fd_set); //标准输入 FD_SET(STDIN_FILENO, &server_fd_set); if (max_fd < STDIN_FILENO) { max_fd = STDIN_FILENO; } //服务器端socket FD_SET(server_sock_fd, &server_fd_set); if (max_fd < server_sock_fd) { max_fd = server_sock_fd; } //客户端连接 for (int i = 0; i < CONCURRENT_MAX; i++) { if (client_fds[i]!=0) { FD_SET(client_fds[i], &server_fd_set); if (max_fd < client_fds[i]) { max_fd = client_fds[i]; } } } int ret = select(max_fd+1, &server_fd_set, NULL, NULL, &tv); if (ret < 0) { perror("select 出错\n"); continue; }else if(ret == 0){ printf("select 超时\n"); continue; }else{ //ret为未状态发生变化的文件描述符的个数 if (FD_ISSET(STDIN_FILENO, &server_fd_set)) { //标准输入 bzero(input_msg, BUFFER_SIZE); fgets(input_msg, BUFFER_SIZE, stdin); //输入 ".quit" 则退出服务器 if (strcmp(input_msg, QUIT_CMD) == 0) { exit(0); } for (int i=0; i 0) { int index = -1; for (int i = 0; i < CONCURRENT_MAX; i++) { if (client_fds[i] == 0) { index = i; client_fds[i] = client_socket_fd; break; } } if (index >= 0) { printf("新客户端(%d)加入成功 %s:%d \n",index,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port)); }else{ bzero(input_msg, BUFFER_SIZE); strcpy(input_msg, "服务器加入的客户端数达到最大值,无法加入!\n"); send(client_socket_fd, input_msg, BUFFER_SIZE, 0); printf("客户端连接数达到最大值,新客户端加入失败 %s:%d \n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port)); } } } for (int i = 0; i 0) { if (byte_num > BUFFER_SIZE) { byte_num = BUFFER_SIZE; } recv_msg[byte_num] = ' '; printf("客户端(%d):%s\n",i,recv_msg); }else if(byte_num < 0){ printf("从客户端(%d)接受消息出错.\n",i); }else{ FD_CLR(client_fds[i], &server_fd_set); client_fds[i] = 0; printf("客户端(%d)退出了\n",i); } } } } } } return 0; }
客户端
#include#include #include #include #include #include #include #define BUFFER_SIZE 1024 int main (int argc, const char * argv[]) { struct sockaddr_in server_addr; server_addr.sin_len = sizeof(struct sockaddr_in); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(11332); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server_addr.sin_zero),8); int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_sock_fd == -1) { perror("socket error"); return 1; } char recv_msg[BUFFER_SIZE]; char input_msg[BUFFER_SIZE]; if (connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in))==0) { fd_set client_fd_set; struct timeval tv; tv.tv_sec = 20; tv.tv_usec = 0; while (1) { FD_ZERO(&client_fd_set); FD_SET(STDIN_FILENO, &client_fd_set); FD_SET(server_sock_fd, &client_fd_set); int ret = select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv); if (ret < 0 ) { printf("select 出错!\n"); continue; }else if(ret ==0){ printf("select 超时!\n"); continue; }else{ if (FD_ISSET(STDIN_FILENO, &client_fd_set)) { bzero(input_msg, BUFFER_SIZE); fgets(input_msg, BUFFER_SIZE, stdin); if (send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1) { perror("发送消息出错!\n"); } } if (FD_ISSET(server_sock_fd, &client_fd_set)) { bzero(recv_msg, BUFFER_SIZE); long byte_num = recv(server_sock_fd,recv_msg,BUFFER_SIZE,0); if (byte_num > 0) { if (byte_num > BUFFER_SIZE) { byte_num = BUFFER_SIZE; } recv_msg[byte_num] = ' '; printf("服务器:%s\n",recv_msg); }else if(byte_num < 0){ printf("接受消息出错!\n"); }else{ printf("服务器端退出!\n"); exit(0); } } } } } return 0; }
当然select也有其局限性。当fd_set中的文件描述符较少,或者大都数文件描述符都比较活跃的时候,select的效率还是不错的。Mac系统中已经定义了fd_set 最大可以容纳的文件描述符的个数为1024
//sys/_structs.h #define __DARWIN_FD_SETSIZE 1024 ///////////////////////////////////////////// //Kernel.framework sys/select.h #define FD_SETSIZE __DARWIN_FD_SETSIZE
每一次select 调用的时候,都涉及到user space和kernel space的内存拷贝,且会对fd_set中的所有文件描述符进行遍历,如果所有的文件描述符均不满足,且没有超时,则当前进程便开始睡眠,直到超时或者有文件描述符状态发生变化。当文件描述符数量较大的时候,将耗费大量的CPU时间。所以后来有新的方案出现了,如windows2000引入的IOCP,Linux Kernel 2.6中成熟的epoll,FreeBSD4.x引入的kqueue。
收藏的用户(0) X
正在加载信息~
推荐阅读
iOS开源 - MDTabbar : Material Design 风格导航栏 iOS 实现
最新回复 (0)
站点信息
- 文章2305
- 用户1336
- 访客11455538
每日一句
Talent without working hard is nothing.
没有努力,天份不代表什么。
没有努力,天份不代表什么。
MySQL 数据库优化
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de
免ROOT实现模拟点击任意位置
Mobaxterm终端神器
CreateProcessW要注意的细节问题
Autonomous NAT Traversal
【教程】win10 彻底卸载edge浏览器
eclipse工程基于Xposed的一个简单Hook
排名前5的开源在线机器学习
Mac OS最简单及(Karabiner)快捷键设置
发一款C++编写的麻将
VMware NAT端口映射外网访问虚拟机linux
独家发布最新可用My-AutoPost——wordpress 采集器
新会员