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. 优点:跨平台
相关推荐
wanhengidc16 小时前
云手机能够流畅运行大型游戏吗
运维·服务器·游戏·智能手机·云计算
繁华的地方不一定留下你的脚印17 小时前
ubuntu18.04版本配置静态IP并且可以上网(解决配置静态IP不能额上网的问题)
运维·服务器
0和1的舞者17 小时前
网络通信的奥秘:HTTP详解 (七)
服务器·网络·网络协议·http·okhttp·软件工程·1024程序员节
阿猿收手吧!19 小时前
windows本机vscode通过ssh免密登录远程linux服务器 && git push/pull 免密
服务器·windows·vscode
创业之路&下一个五年19 小时前
按照ip的转换为二进制的方式理解a\b\c类地址的边界
服务器·网络·tcp/ip
skywalk816319 小时前
尝试Auto-coder.chat使用星河社区AIStudio部署的几个大模型:文心4.5-21b、Deepseek r1 70b、llama 3.1 8b
linux·服务器·人工智能·大模型·aistudio
梁正雄21 小时前
6、prometheus资源规划
运维·服务器·服务发现·prometheus·监控
晨曦之旅21 小时前
零成本体验云计算!阿贝云免费服务器深度测评
运维·服务器·云计算
神仙别闹1 天前
基于C语言 HTTP 服务器客户端的实验
服务器·c语言·http
初听于你1 天前
运维高级故障排除与恢复-SysRq
运维·服务器·安全