linux 多路转接之select
2017-06-09 16:00
387 查看
一、 select函数
1.定义
select:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。
2.作用
(1)系统提供select函数来实现多路复用输入/输出模型。
(2)select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。
3.接口参数
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的全部位
(1)nfds:待测试描述符个数。最大文件描述符+1。
(2)readfds:需要检测的可读文件件描述符集。
(3)writefds:可写文件描述符集。
(4)exceptfds:异常文件描述符集。(产生异常的原因有两个:1.某个套接字的带外数据的到达;2.某个已置为分组模式的伪终端存在可从其主端读取的控制状态信息)
(5)timeout:它告知内核等待所制定描述符中的任何一个就绪可在多长时间,其timeval结构用于指定这段时间的秒数和微妙数。
①永远等待下去(*timeout == NULL):
仅在有一个描述符准备好I/O时才返回。
②等待固定时间(timeout->tv_sec != 0 && timeout->tv_usec != 0):
在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。
③根本不等待(timeout->tv_sec == 0 && timeout->tv_usec == 0):检查描述符后立即返回,这称为轮询。
前两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回。
4. 优缺点
优点:相比起多进程/线程 缩短了等待时间,提高效率,减少进程占用内存开销
缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件件描述符数量太小了,默认是1024
二、dup
(1)定义
dup:用来复制oldfd所指的文件描述符。但复制成功时返回最小的尚未被使用的文件描述符。若有错误则返回-1,错误代码存入errno中。返回的新文件描述符和参数oldfd指向同一个文件,共享所有的锁定,读写指针,和各项权限或标志位。
(2)实际操作
1.打开一个新文件
2.关掉标准输出文件符
3.调用dup给文件描述符
4.此时文件描述符变为1
5.将所要打印数据重定向到文件中
三、代码
1.sever端
2.client端
1.定义
select:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。
2.作用
(1)系统提供select函数来实现多路复用输入/输出模型。
(2)select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。
3.接口参数
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的全部位
(1)nfds:待测试描述符个数。最大文件描述符+1。
(2)readfds:需要检测的可读文件件描述符集。
(3)writefds:可写文件描述符集。
(4)exceptfds:异常文件描述符集。(产生异常的原因有两个:1.某个套接字的带外数据的到达;2.某个已置为分组模式的伪终端存在可从其主端读取的控制状态信息)
(5)timeout:它告知内核等待所制定描述符中的任何一个就绪可在多长时间,其timeval结构用于指定这段时间的秒数和微妙数。
struct timeval { long tv_sec;//seconds long tv_usec;//microseconds }
①永远等待下去(*timeout == NULL):
仅在有一个描述符准备好I/O时才返回。
②等待固定时间(timeout->tv_sec != 0 && timeout->tv_usec != 0):
在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。
③根本不等待(timeout->tv_sec == 0 && timeout->tv_usec == 0):检查描述符后立即返回,这称为轮询。
前两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回。
4. 优缺点
优点:相比起多进程/线程 缩短了等待时间,提高效率,减少进程占用内存开销
缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件件描述符数量太小了,默认是1024
二、dup
(1)定义
dup:用来复制oldfd所指的文件描述符。但复制成功时返回最小的尚未被使用的文件描述符。若有错误则返回-1,错误代码存入errno中。返回的新文件描述符和参数oldfd指向同一个文件,共享所有的锁定,读写指针,和各项权限或标志位。
(2)实际操作
1.打开一个新文件
2.关掉标准输出文件符
3.调用dup给文件描述符
4.此时文件描述符变为1
5.将所要打印数据重定向到文件中
三、代码
1.sever端
#include<stdio.h> #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> #include<stdlib.h> #include<string.h> int array_fds[1024]; static void Usage(char* proc) { printf("Usage: %s [local_ip] [local_port]", proc); } int startup(char* _ip, int _port) { int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) { perror("socket"); return 2; } int flg = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flg, sizeof(flg)); struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(_port); local.sin_addr.s_addr = inet_addr(_ip); if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) { perror("bind"); return 3; } if(listen(sock, 5) < 0) { perror("listen"); return 0; } return sock; } int main(int argc, char* argv[]) { if(argc != 3) { Usage(argv[0]); return 1; } int listen_socket = startup(argv[1], atoi(argv[2])); fd_set rfds; int max_fd = 0; int array_size = sizeof(array_fds)/sizeof(array_fds[0]); array_fds[0] = listen_socket; int i = 0; for(i = 1; i < array_size; i++) { array_fds[i] = -1; } while(1) { max_fd = -1; struct timeval timeout = {0,0}; FD_ZERO(&rfds); for(i = 0; i < array_size; i++) { if(array_fds[i] > 0) { FD_SET(array_fds[i], &rfds); if(array_fds[i] > max_fd) { max_fd = array_fds[i]; } } } switch(select(max_fd+1, &rfds, NULL,NULL, NULL)) { case 0: printf("timeout...\n"); break; case -1: perror("select"); break; default: { int k = 0; for(;k < array_size; k++) { if(array_fds[k] < 0) continue; if(k == 0 && FD_ISSET(array_fds[k], &rfds)) { struct sockaddr_in client; socklen_t len = sizeof(client); int new_sock = accept(array_fds[k], (struct sockaddr*)&client, &len); if(new_sock < 0) { perror("accept"); continue; } else { printf("get a client : %s , %d \n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); int j = 1; for(;j < array_size;j++) { if(array_fds[j] < 0) { array_fds[j] = new_sock; break; } } if(j == array_size) close(new_sock); } } else if(k != 0 && FD_ISSET(array_fds[k],&rfds)) { char buf[10240]; ssize_t s = read(array_fds[k], buf, sizeof(buf)-1); if(s > 0) { buf[s] = 0; printf("client say# %s \n", buf); write(array_fds[k], buf, strlen(buf)); } else if(s == 0) { printf("client quit ...\n"); close(array_fds[k]); array_fds[k] = -1; } else { perror("read"); close(array_fds[k]); array_fds[k] = -1; } } else { } } } break; } } return 0; }
2.client端
#include<stdio.h> #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> #include<stdlib.h> #include<string.h> #include<strings.h> #include<sys/stat.h> #include<unistd.h> static void Usage(char * proc) { printf("Usage : %s [ip] [port]\n"); } int main(int argc, char* argv[]) { if(argc != 3) { Usage(argv[0]); return 1; } int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in peer; peer.sin_family = AF_INET; peer.sin_port = htons(atoi(argv[2])); peer.sin_addr.s_addr = inet_addr(argv[1]); if(connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0) { perror("connect"); return 2; } char buf[10240]; while(1) { printf("Please Enter : "); fflush(stdout); ssize_t s = read(0, buf, sizeof(buf)-1); int sfd = dup(STDOUT_FILENO); if(s > 0) { buf[s-1] = 0; int new_fd = dup2(sock, 1); if(new_fd == -1) { perror("dup()"); return -1; } printf("%s",buf); fflush(stdout); dup2(sfd, STDOUT_FILENO); ssize_t _s = read(sock, buf, sizeof(buf)-1); if(_s > 0) { buf[_s] = 0; printf("sever # %s \n", buf); } } } close(sock); return 0; }
相关文章推荐
- 【Linux】I/O多路转接select
- Linux的I/O多路转接模型和select()
- 【Linux网络编程】基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
- Linux高性能服务器之多路转接(1)----select模型实现
- Linux下I/O多路转接之select --fd_set
- Linux下I/O多路转接之select --fd_set
- Linux【网络编程】——I/O多路转接之Select服务器
- Linux下高级I/O多路转接之select服务器
- 【Linux】多路 IO转接服务器 — select
- 【Linux】中I/O多路转接之select
- Linux:I/O多路转接之select(有图有代码有真相!!!)
- Linux下I/O多路转接之select --fd_set
- linux的I/O多路转接select的fd_set数据结构和相应FD_宏的实现分析
- I/O多路转接(select、pselect和poll)使用
- UNIX环境高级编程——I/O多路转接(select、pselect和poll)
- 转:linux select 多路复用机制
- linux select 多路复用机制
- linux select 多路复用机制
- linux 多路复用 select (二)
- UNIX环境高级编程——I/O多路转接(select、pselect和poll)