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. 优点:跨平台
相关推荐
天诰4 小时前
SA-K-11 检索会消失吗与四个未解问题
java·服务器·人工智能·python·机器学习·ai编程
陈希1236 小时前
Linux 防火墙配置实战:firewalld、iptables 与云安全组
linux·运维·服务器
女神下凡7 小时前
芯参谋(95):嵌入式文件系统_开发指南
linux·服务器·arm开发·驱动开发·设计规范
@兽血沸腾7 小时前
Windows系统安装Jenkins部署应用至远程Windows服务器操作指南
运维·服务器·jenkins
讯捷蓝达12 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
数字新视界15 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落15 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
lisanmengmeng15 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮16 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹16 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器