IO复用之select概述

函数说明

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

        返回值说明:

                成功:return > 0

                超时:reutrn == 0

                失败:return == -1

        参数说明:

                nfds: 最大监控套接字描述符加1

                readfds|writefds|exceptfds: 需要监控可读|可写|异常文件描述符集合

                timeout: 超时时间,为NULL则阻塞,时间为0则轮询检查文件描述符后立即返回

理解select

        要理解select,必须清楚文件描述符集合在select中的变化及作用。

        操作文件描述符集合的函数,以宏定义方式定义,如下:

                删除:void FD_CLR(int fd, fd_set *set);

                检测:int  FD_ISSET(int fd, fd_set *set);

                添加:void FD_SET(int fd, fd_set *set);

                清空:void FD_ZERO(fd_set *set);

        举例说明:

                fd_set readfds;

                FD_ZERO(&readfds); 表示清空文件描述符集合

                FD_SET(1, &readfds); 表示添加文件描述符1到集合里面

                FD_CLR(1, &readfds); 表示删除文件描述符1从集合里面

                FD_ISSET(1, &readfds); 表示判断描述符1是否在集合里面

        文件描述符集合变量说明,此变量类似于以下定义:

                typedef long long fd_set;

        fd_set变量有一块连续的内存空间,此空间访问按bit访问,如第一bit就表示描述符1,第二bit就表示2,以此类推,描述符在集合中就表示对应bit置为1,反之为0.

        现在,举例说明select进行时集合变化,以集合set第一字节说明。

        1)fd_set set清空,FD_ZERO(&set); set第一字节变为00000000.

        2)设置fd = 5, FD_SET(fd, &set); set第一字节变化为00010000.

        3)设置fd = 2, FD_SET(fd, &set); set第一字节变化为00010010.

        4)执行select(6, &set, NULL, NULL, NULL)阻塞等待

        5)若fd = 2发生时间,select返回,此时set第一字节变为00000010,fd = 5被清空。

        通过以上步骤,可以知道,文件描述符集合在调用select是会变化的,在select返回只会返回发生事件的文件描述符。

select优缺点

        优点:通过事件驱动,可以在一个线程监控多个文件描述符。

        缺点:1、文件描述符有数量限制,一般linux默认1024

                  2、开销过大,每次调用select都会从用户态拷贝集合进入内核,并且内核是遍历所有文件描述符,开销过大

代码说明

void handle_select(int sockfd)
{
	int i, result = 0, maxfd = sockfd;
	int clientfds[10] = {-1};
	struct timeval tv;
	fd_set readfds;
	listen(sockfd, 10);

	tv.tv_sec = 1;
	tv.tv_usec = 0;

	while (1) {
		FD_ZERO(&readfds);
		FD_SET(sockfd, &readfds);
		maxfd = sockfd;

		for (i = 0; i < 10; i++) {
			if (clientfds[i] == -1)
				continue;
			FD_SET(clientfds[i], &readfds);
			if (maxfd < clientfds[i])
				maxfd = clientfds[i];
		}

		result = select(maxfd, &readfds, NULL, NULL, &tv);
		if (result == 0)
			continue;
		if (result == -1)
			break;
		for (i = 0; i < 10; i++) {
			if (clientfd[i] == -1)
				continue;
			if (FD_ISSET(clientfds[i], &readfds)) {
				char buf[512] = {0};
				result = read(clientfds[i], buf, 512);
				if (result <= 0) {
                    close(clientfds[i]);
					clientfds[i] = -1;
					continue;
				} else if (result > 0) {
					printf("recv data, data = %s\n", buf);
					write(clientfds[i], "hello client!", strlen("hello client!") + 1);
				}
			}
		}
		if (FD_ISSET(sockfd, &readfds)) {
			struct sockaddr_in clientaddr;
			bzero(&clientaddr, sizeof(clientaddr));
			socklen_t clientlen = sizeof(clientaddr);
			int clientfd = accept(sockfd, &clientaddr, &clientlen);
			if (clientfd > 0) {
				for (i = 0; i < 10; i++) {
					if (clientfds[i] == -1) {
						clientfds[i] = clientfd;
						break;
					}
				}
				if (i == 10) {
					printf("too man clientfd!");
				}
			}
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值