linux网络编程十三:I/O复用select
来源:互联网 发布:淘宝企业店铺升级流程 编辑:程序博客网 时间:2024/06/11 06:11
最近在看《linux高性能服务器编程》,在此做个日记,以激励自己,同时分享于有需要的朋友。
I/O复用使得程序能够同时监听多个文件描述符,对提高程序的性能至关重要。
通常,网络程序在下列情况下需要使用I/O复用技术:
A. 客户端程序要同时处理多个socket。
B. 客户端程序要同时处理用户输入和网络连接。比如:聊天室
C. TCP服务器要同时监听socket和连接socket。
D. 服务器要同时处理TCP请求和UDP请求。
E. 服务器要同时监听多个端口, 或者处理多种服务。比如:xinetd服务
虽然I/O复用能同时监听多个文件描述符,但它本身是阴塞的。并且当多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能顺序依次处理其中的每个文件描述符,这使得服务器程序看起来像是串行工作。如果要实现并发,只能使用多进程或多线程。
linux下实现I/O复用的系统调用有select、poll、epoll,今天我们讨论select。
1. select函数
#include <sys/socket.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds参数指定被监听的文件描述符的总数,通常设置为select监听的所有文件描述中最大值+1,因为文件描述符是从0开始的。
readfds, writefds, exceptfds参数分别指可读,可写和异常文件描述符。select调用返回时,内核将修改它们来通知程序哪些文件描述符已经就绪。它们都是fd_set结构指针,它仅包含一个整型数组,该数组的每个元素的每一位(bit)标记一个文件描述符。fd_set能容纳的最大文件描述符数量由FD_SETSIZE指定,这就限制了select能同时处理的文件描述符的总量。
我们使用一系列宏来访问fd_set结构体:
#include <sys/select.h>FD_ZERO(fd_set *fdset); //清除fdset所有位FD_SET(int fd, fd_set *fdset); //设置fdset的位fdFD_CLR(int fd, fd_set *fdset); //清除fdset的位fdint FD_ISSET(int fd, fd_set *fdset); //测试fdset的位fd是否被设置
timeout参数设置select的超时时间。这是timeval结构指针,用来告诉内核select等待多久。
struct timeval{ long tv_sec; \\秒数 long tv_usec; \\微秒}
如果都为0,则select立即返回。如果给timeval传NULL,则一直阻塞。
调用成功时返回就绪文件描述符的总数。如果在超时时间内没有任何文件描述符就绪,返回0。失败时返回-1,并设置errno。如果在select等待期间,程序收到信号,则立即返回-1,并设置errno为EINTR。
2. 代码,用select同时接收普通数据和外带数据
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <assert.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>int main(int argc, char **argv){if (argc != 3) {fprintf(stderr, "Usage: %s ip port\n", basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);printf("ip is %s and port is %d\n", ip, port);int ret = 0;struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;address.sin_port = htons(port);inet_pton(AF_INET, ip, &address.sin_addr);int listenfd = socket(PF_INET, SOCK_STREAM, 0);assert(listenfd >= 0);ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));assert(ret != -1);ret = listen(listenfd, 5);assert(ret != -1);struct sockaddr_in client_address;socklen_t client_addrlength = sizeof(client_address);int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);if (connfd < 0) {printf("error: %s\n", strerror(errno));close(listenfd);}char remote_addr[INET_ADDRSTRLEN];printf("connect with ip: %s and port: %d\n", inet_ntop(AF_INET, &client_address.sin_addr, remote_addr, INET_ADDRSTRLEN), ntohs(client_address.sin_port));char buf[1024];fd_set read_fds;fd_set exception_fds;FD_ZERO(&read_fds);FD_ZERO(&exception_fds);//设置接收处带数据int reuseaddr = 1;setsockopt(connfd, SOL_SOCKET, SO_OOBINLINE, &reuseaddr, sizeof(reuseaddr));while (1) {memset(buf, '\0', sizeof(buf));//每次调用select前都要重新设置,因为事件发生后,文件描述符集合会被内核修改FD_SET(connfd, &read_fds);FD_SET(connfd, &exception_fds);ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);printf("select one\n");if (ret < 0) {printf("selection failed\n");break;}if (FD_ISSET(connfd, &read_fds)) {ret = recv(connfd, buf, sizeof(buf)-1, 0);if (ret <= 0)break;printf("get %d bytes of normal data: %s\n", ret, buf);}else if (FD_ISSET(connfd, &exception_fds)) {//接收外带数据ret = recv(connfd, buf, sizeof(buf)-1, MSG_OOB);if (ret <= 0)break;printf("get %d bytes of oob data: %s\n", ret, buf);}}close(connfd);close(listenfd);return 0;}
- linux网络编程十三:I/O复用select
- linux 网络编程 I/O复用 select,poll ,epoll
- Linux网络编程---I/O多路复用 之 select
- Linux网络编程---I/O复用模型之select
- 嵌入式linux网络编程之I/O多路复用select
- Linux网络编程(三)——select函数实现I/O复用(传输文件)
- Linux网络编程之I/O复用
- WIN网络编程-select(I/O模型)
- Linux编程I/O复用select用法备忘
- 【Unix 网络编程】服务器网络编程模型——I/O复用:select 函数
- 网络编程:I/O复用
- 网络编程:I/O复用
- linux网络编程之socket(八):五种I/O模型和select函数简介
- linux网络编程之socket(八):五种I/O模型和select函数简介
- Linux网络编程 五种I/O 模式及select、epoll方法的理解
- Linux网络编程 五种I/O 模式及select、epoll方法的理解
- Linux网络编程——I/O复用之select详解
- Linux网络编程——tcp并发服务器(I/O复用之select)
- zoj 3725 Painting Storages 题解
- codeforces#239_div2_B Garland 简单模拟
- C语言头文件的使用
- 作业(第四周)
- ZOJ-3116
- linux网络编程十三:I/O复用select
- linux服务器 通过nfs挂载存储需要开portmap服务
- getApplication和Activity.this的区别
- UML中6大关系分析
- 改善你的电池寿命十倍PureMotive
- 归并排序
- poj3437
- 环境: ubuntu 12.04 上C / C++, OpenGL, Android, NDK,Ogre环境配置(此贴作废,配置看我新贴)
- 虚函数与构造函数析构函数