select多路IO复用

select多路IO复用的应用场景:

  1. 一个进程同时需要同时监听处理多个IO事件的场景
  2. 一个服务器即要处理TCP又要UDP

优点:

  1. 无需创建多个进程或线程单独与客户端对接,减少系统开销
  2. 扩平台、可移植强

缺点

  1. select监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数
  2. 解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力

1. select

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

nfds:监听的所有文件描述符中,最大文件描述符+1
readfds: 读文件描述符监听集合。 传入、传出参数
writefds: 写文件描述符监听集合。 传入、传出参数 (不常用) NULL
exceptfds: 常文件描述符监听集合 传入、传出参数 NULL
timeout: 设置监听超时时长 (NULL: 阻塞监听)(0: 非阻塞监听,轮询)
返回值
> 0: 所有监听集合(3个)中, 满足对应事件的总数。
0: 没有满足监听条件的文件描述符
-1: errno

2. FD_ZERO

功能: 清空一个文件描述符集合

void FD_ZERO(fd_set *set);

3. FD_SET

功能:将待监听的文件描述符,添加到监听集合中

void FD_SET(int fd, fd_set *set);

4. FD_CLR

功能: 将一个文件描述符从监听集合中 移除。

void FD_CLR(int fd, fd_set *set);

5. FD_ISSET

功能:判断一个文件描述符是否在监听集合中

int  FD_ISSET(int fd, fd_set *set);

返回值: 在:1;不在:0

demo

#include "cs_dev.h"
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

#define SOCKPROT 8888

int main(void)
{
	int sockfd = 0;
	int c_fd   = 0;
	char dstIp[16] = {0};
	int c_addr_len = 0;
	char buf[1024] = {0};
	struct sockaddr_in sockaddr;
	struct sockaddr_in c_addr;

	c_addr_len = sizeof(c_addr);

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	Setsockopt(sockfd);		 //设置端口复用
	Bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
	Listen(sockfd, 10);

	int j = 0;
	fd_set readfds, allfds;
	int nread = 0;
	int nready = 0;
	int maxfd  = sockfd;
	int client[FD_SETSIZE];  //客户端连接文件描述符数组
	int maxi = -1;           //记录client数组最大下标位置

	for (int i=0; i < FD_SETSIZE; i++) {
		client[i] = -1;
	}

	FD_ZERO(&readfds);
	FD_SET(sockfd, &readfds);
	allfds = readfds;        //select返回 readfds修改为监听到的文件描述符集合; allfds作为备份

	while (1) {
		readfds = allfds;    
		nready = select(maxfd + 1, &readfds, NULL, NULL, NULL); //阻塞等待,成功返回 nready > 0
		if (nread == -1) 
			perr_exit("select");

		if (FD_ISSET(sockfd, &readfds)) {     //新连接处理
			c_fd = Accept(sockfd, (struct sockaddr*)&c_addr, &c_addr_len);
			printf("IP:%s PROT:%d connect...\n", \
					inet_ntop(AF_INET, &c_addr.sin_addr, dstIp, sizeof(dstIp)), \
					ntohs(c_addr.sin_port));

			for (j=0; j < FD_SETSIZE; j++) {  //将新连接的客户端文件描述符存放入client数组
				if (client[j] == -1) {
					client[j] = c_fd;
					break;
				}
			}
			if (j == FD_SETSIZE)	//超过最大连接数
				fputs("too many clients connect...\n", stderr);
			else if (c_fd > maxfd)  //获取最大文件描述符
				maxfd = c_fd;
	
			FD_SET(c_fd, &allfds);  //加入到备份集合
	
			if (j > maxi) 			//更新客户端文件描述数组最大值下标
				maxi = j;

			if (--nready == 0)      //事件处理完毕
				continue;
		}

		for (int i=0; i <= maxi; i++) {  //读数据
			if ((client[i] != -1) && (FD_ISSET(client[i], &readfds))) {
				memset(buf, 0, sizeof(buf));
				nread = Read(client[i], buf, sizeof(buf));
				if (nread == 0) {        //客户端断开连接
					Close(client[i]);
					FD_CLR(client[i], &allfds);
					client[i] = -1;
					for (; i >= 0; i--) {//更新客户端文件描述数组最大值下标
						if (client[i] != -1) {
							maxi = i;
							break;
						}
					}
				} else if (nread > 0) {  //处理动作
					Write(client[i], buf, nread);
					Write(0, buf, nread);
				}

				if (--nready == 0)		 //事件处理完毕
					break;
			}
		}
	}

	Close(sockfd);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多路 I/O 复用是一种在系统编程中常用的技术,它允许一个进程同时监视多个 I/O 事件,以提高程序的效率和响应能力。在 C 语言中,一个常用的多路 I/O 复用函数是 `select`。 `select` 函数的原型如下: ```c #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` 参数说明: - `nfds`:监视的文件描述符集合中最大的文件描述符值加1。 - `readfds`:读事件的文件描述符集合。 - `writefds`:写事件的文件描述符集合。 - `exceptfds`:异常事件的文件描述符集合。 - `timeout`:超时时间,如果设置为 NULL,则阻塞直到有事件发生;如果设置为零,立即返回;如果设置为一个指定时间,超过该时间还没有事件发生,则返回。 `select` 函数的工作原理是将进程阻塞,直到监视的文件描述符集合中的任意一个文件描述符就绪(可读、可写或出现异常),或者超过指定的超时时间。 使用 `select` 函数进行多路 I/O 复用的一般步骤如下: 1. 创建并初始化文件描述符集合。 2. 将需要监视的文件描述符添加到相应的集合中。 3. 调用 `select` 函数进行阻塞等待。 4. 检查哪些文件描述符已经就绪。 5. 处理就绪的文件描述符。 需要注意的是,`select` 函数在每次调用时都会修改传入的文件描述符集合,因此在每次调用前需要重新初始化。 除了 `select`,还有其他的多路 I/O 复用函数,如 `poll` 和 `epoll`,它们在不同的操作系统中有不同的实现方式和特性,可以根据具体需求选择合适的函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值