5、select多路IO转接

高并发服务器

多线程/进程服务器实现思路:
server lfd cfd1 cfd2 client1 client2

但是这样做所有客户端直接连服务器占用资源太多,所以有以下另一种方式。
server select lfd client1 client2 client3

​ 使用select来管理客户端。

​ 当客户端有需要连接请求时,才会向select申请。此时select向sever报备,sever调用accept函数
监管read/write client select lfd server 产生cfd

select也称为,响应模式---多路IO转接

1、select参数简介:

cpp 复制代码
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struvt timeval *time out);
/*
nfds:监听的所有文件描述符当中最大的文件描述符的编号 + 1
在select的内部有一个循环,循环的次数就是文件描述符的上限
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,均是传入传出参数
exceptfds时异常事件
*/

在文件描述符表中,当程序启动时,默认被占用

0 启动被占用
1 启动被占用
2 启动被占用
3 lfd
4 cfd1
5 cfd2
6 cfd3

readfds,writefds:

​ 当客户端发送请求时,lfd需要读这个请求。同理,有时也会给客户端写数据。 fd_set中的set的意思是集合。这两个是传入传出参数,比如writefds有三个{4,5,6},但是如果在监听时只有两个满足写事件,其就会传出{4,5}。

​ select的返回值是指在监听的时候满足时间的个数。

​ 传入和传出的参数可以理解为一个位图:

传入:监控写文件描述符{3,5,6}

0 1 2 3 4 5 6 7 8
0 0 0 1 0 1 1 0 0

传出:监控写文件描述符{5,6}

0 1 2 3 4 5 6 7 8
0 0 0 0 0 1 1 0 0

2、select相关参数分析

cpp 复制代码
void FD_ZERO(fd_set *set); //全部置0
void FD_SET(int fd, fd_set *set);//将待监听的文件描述字加入
void FD_CLR(int fd, fd_set *set);//清楚某一个文件描述符
void DS_ISSET(int fd, fd_set *set);//添加文件描述符
int FD_ISSET(int fd, fd_set *set);//判断一个文件描述字是否在监听集合中
cpp 复制代码
int select(int nfds, fd set *readfds, fd set *writefds, fd_set *exceptfds, struct timeval *timecut);
/*
nds:监听的所有文件描述符中,最大文件描述符+1
传入、传出参数
readfds:读文件描述符监听集合。
writefds,写文件描述符监听集合。传入、传出参数nulL
exceptfds,异常文件描述符监听集合NULL
timeout:
>0:设置监听超时日长
NUII:阻赛监听
0:非阻害监听,轮询
返回值:
>0:所有监听集合 (3个)中, 满对应事件的点数。0:
0:没有满足监听条件的文件描述符0
-1:errno
*/

3、select实现多路IO转接思路

思路伪代码:

cpp 复制代码
lfd = socket();
bind();
listen();
fd_set allset;
fd_set rset;//创建读集合
FD_ZERO(&allrset);//将读监听集合清空
FD_SET(lfd,&allrset);//将lfd添加到读集合当中
while(1)
{
    rset = allset;
	ret = select(lfd+1,&rset,NULL,NULL,NULL);//监听文件描述符集合对应的事件
	if(ret > 0)//有监听的描述满足对应事件
	{
    	if(FD_ISSET(lfd,&rset))//1在,0不在,说明建立完结完成
        {
            cfd = accept();
            FD_SET(cdf,&allset);
        }
        for(i=lfd+1; i < 最大文件描述符;i++)
        {
            FD_ISSET(i,&rset); //有read、write事件
            read();
            toUpper();
            write();  
        }
	}
}

代码:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>

#define SERV_PORE 8086

int main(int argc, char *argv[])
{
    int i,j,n,nready;
    int cfd;
    int maxfd = 0;
    char buf[BUFSIZ];

    struct sockaddr_in clie_addr,serv_addr;
    socklen_t clie_addr_len;
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    int opt = 1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    bzero(&serv_addr,sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORE);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    listen(lfd,128);

    fd_set rset,allset;
    maxfd = lfd;
    FD_ZERO(&allset);
    FD_SET(lfd,&allset);
    while(1)
    {
        rset = allset;
        nready = select(maxfd+1,&rset,NULL,NULL,NULL);
        if(FD_ISSET(lfd,&rset))
        {
            clie_addr_len = sizeof(clie_addr);
            cfd = accept(lfd,(struct sockaddr *)&clie_addr,&clie_addr_len);
            FD_SET(cfd,&allset);//必须设定在拷贝集合当中
            if(maxfd < cfd)
                maxfd = cfd;
            if(0 == --nready)//必须有这一项,保证当只有lfd是会跳过后面的for循环
                continue;
        }
        for(i = lfd+1; i<= maxfd; i++)
        {
            if(FD_ISSET(i,&rset))
            {
                if((n = read(i,buf,sizeof(buf))) == 0)
                {
                    close(i);
                    FD_CLR(i,&allset);
                }
                else if(n > 0)
                {
                    for(j = 0; j < n; j++)
                    {
                        buf[j] = toupper(buf[j]);
                    }
                    write(i,buf,n);
                }
            }
        }
    }
}

4、select优缺点

  1. select监听是使用轮询实现的
  2. 监听受文件描述符限制,最大为1024
  3. 检测满足条件的cfd,只能自己添加业务逻辑,提高了编码难度
  4. 优点:跨平台
相关推荐
Orange_sparkle2 分钟前
Windows/Linux离线部署IndexTTS2
linux·运维·服务器
..空空的人6 分钟前
C++基于protobuf实现仿RabbitMQ消息队列---技术认识1
服务器·网络·个人开发·protobuf·muduo
hjs_deeplearning26 分钟前
应用篇#4:Qwen2视觉语言模型(VLM)的服务器部署
服务器·人工智能·python·深度学习·语言模型
Aze..28 分钟前
PVE安装 Ubuntu 服务器版
运维·服务器·数据库
zt1985q44 分钟前
fnOS 飞牛云 NAS 本地部署开源 TTS 文本转语音工具 EasyVoice 并实现外部访问
运维·服务器·网络协议·开源
Xyz996_1 小时前
Keepalived双机热备
linux·运维·服务器
Han.miracle1 小时前
网络层-IP 协议全景解析:从地址分配到内网穿透,解锁互联网通信底层密码
服务器·网络·网络协议·ip
I · T · LUCKYBOOM1 小时前
23.ssh远程连接、TCP策略
linux·运维·服务器·网络·tcp/ip·ssh
Vect__1 小时前
Linux常见指令
linux·服务器
福尔摩斯张1 小时前
C语言文件操作详解(一):文件的打开与关闭(详细)
java·linux·运维·服务器·c语言·数据结构·算法