• 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);
    
    • 1
    • 2

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

    2. FD_ZERO

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

    void FD_ZERO(fd_set *set);
    
    • 1

    3. FD_SET

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

    void FD_SET(int fd, fd_set *set);
    
    • 1

    4. FD_CLR

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

    void FD_CLR(int fd, fd_set *set);
    
    • 1

    5. FD_ISSET

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

    int  FD_ISSET(int fd, fd_set *set);
    
    • 1

    返回值: 在: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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
  • 相关阅读:
    silk 编解码器下载
    如何将webp格式转换成png
    网络安全(黑客技术)——自学思路
    Vue3 学习总结笔记 (十四)
    Halcon (0):C# 联合Halcon方式简介和就业市场说明
    Console 线连接路由器交换机等设备
    prometheus:(二)监控概述(你永远逃不出我的手掌哈哈)
    云流化:XR扩展现实应用发展的一个新方向!
    计算机毕业论文微信小程序毕业设计SSM校园生活小助手+后台管理系统|前后分离VUE校园网站[包运行成功]
    ipsec vxn详解
  • 原文地址:https://blog.csdn.net/weixin_54178481/article/details/125546113