高并发服务器-多路IO转接-select

多路IO转接select

概述

多路IO转接服务器也叫做多任务IO服务器。该类服务器实现的主旨思想是,不再由应用程序自己监视客户端连接,取而代之由内核替应用程序监视文件。

文章目录

select

  1. select能监听的文件描述符个数受限于FD_SETSIZE ,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数

  2. 解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力。

相关函数

cpp 复制代码
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
			fd_set *exceptfds, struct timeval *timeout);

	nfds: 		监控的文件描述符集里最大文件描述符加1
	readfds:	监控有读数据到达文件描述符集合,传入传出参数
	writefds:	监控写数据到达文件描述符集合,传入传出参数  NULL
	exceptfds:	监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数 NULL
	timeout:	定时阻塞监控时间,3种情况   //超时处理。
				1.NULL,永远等下去
				2.设置timeval, > 0 等待固定时间
				3.设置timeval里时间均为0,检查描述字后立即返回,轮询
	struct timeval {
		long tv_sec; /* seconds */
		long tv_usec; /* microseconds */
	};
	void FD_CLR(int fd, fd_set *set); 	//把文件描述符集合里fd位清0
	int FD_ISSET(int fd, fd_set *set); 	//测试文件描述符集合里fd是否置1
	void FD_SET(int fd, fd_set *set); 	//把文件描述符集合里fd位置1
	void FD_ZERO(fd_set *set); 			//把文件描述符集合里所有位清0

辅助函数

c 复制代码
fd_set set;
// 清空
void FD_ZERO(fd_set *set);
// 将 fd 从 set集合中清除出去。
void FD_CLR(int fd, fd_set *set);
// 将 fd 添加到监听结合 set中
void FD_SET(int fd, fd_set *set);
// 判断 fd 是否在集合set中
int  FD_ISSET(int fd, fd_set *set);
	返回:在:1. 不在:0

// 举例:
fd_set rset;		// 创建 read事件监听集合
FD_ZERO(&rset);		// 清空 读集合
FD_SET(lfd, &rset);	// 将 lfd 添加到读集合
FD_SET(cfd1, &rset);	// 将 cfd1 添加到读集合
FD_SET(cfd3, &rset);	// 将 cfd3 添加到读集合
while (1) {
	nready = select(cfd3+1, &rset, NULL, NULL, NULL);
}

select 优缺点

  • 缺点:

    • select 返回值不能描述,具体满足对应事件的描述符是哪一个!
    • 需要使用 遍历方法,找寻那个满足事件的 fd。
    • 在一个进程中,最多只支持 1024 客户端链接请求。
  • 优点:

    • 跨平台!win、Linux、MacOs、Unix、类Unix、mips ...
  • 特性:select 实现多路IO转接并发服务器的性能,并不比 poll、epoll 低。

练习

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

#define SRV_PORT 10086
#define BUF_SIZE 1024
int test01()
{
    int ret = -1;
    int lfd = -1;
    int cfd = -1;
    int maxfd = 0;

    char buf[BUF_SIZE];
    //初始化服务器地址
    struct sockaddr_in srv_addr;
    memset(&srv_addr, 0, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons(SRV_PORT);
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
	//用于接收客户端地址
    struct sockaddr_in clt_addr;
     socklen_t clt_addr_len = sizeof(clt_addr);

     fd_set rset; // 读集合
     fd_set allset;//监听集合

    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == lfd)
    {
        perror("socket");
    }
    
    bind(lfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
    listen(lfd, 128);

    maxfd = lfd;

    //清空监听集合
    FD_ZERO(&allset);
    //将lfd 添加到集合中
    FD_SET(lfd, &allset);

    while(1)
    {
        //
        rset = allset;
        ret = select(maxfd+1, &rset, NULL, NULL, NULL); //监听读集合
        if(ret < 0)
        {
            perror("select");
        }
        //判断lfd 是否在传出的读集合中
        if(FD_ISSET(lfd, &rset))
        {
            //满足都事件
            cfd = accept(lfd, (struct sockaddr*)&clt_addr, &clt_addr_len);
            FD_SET(cfd, &allset);//将通信的cfd 添加到监听集合中
            if(maxfd < cfd) //更新maxfd
            {
                maxfd = cfd;
            }
            if(ret == 1) //只有一个fd满足读事件,并且已经处理完成
            {
                continue;
            }
        }
        //处理数据通信
        for(int i = lfd + 1; i < maxfd + 1; i ++)
        {
            //判断读集合中的 cfd
            if(FD_ISSET(i, &rset))
            {
                ret = read(i, buf, BUF_SIZE);
                if(0 == ret)
                {           
                    close(i);
                    FD_CLR(i, &allset);  //移除已关闭的cfd
                }

                for(int j = 0; j < ret; ++j)
                {
                    buf[j] = toupper(buf[j]);
                }
                write(i, buf, ret);
                //输出到屏幕
                write(STDOUT_FILENO, buf, ret);
            }
        }
    }
    close(lfd);
    return 0;
}
相关推荐
口嗨农民工3 小时前
linux 时间相关的命令
linux·运维·服务器
阿加犀智能3 小时前
使用Langchain生成本地rag知识库并搭载大模型
服务器·python·langchain
楠枬3 小时前
DNS 域名解析
服务器·网络·网络协议
XMZH030423 小时前
网络编程;TCP多进程并发服务器;TCP多线程并发服务器;TCP网络聊天室和UDP网络聊天室;后面两个还没写出来;0911
服务器·网络·tcp/ip·udp·tcp
云飞云共享云桌面4 小时前
1台电脑10个画图设计用怎么实现
linux·运维·服务器·网络·数据库·自动化·电脑
艾莉丝努力练剑4 小时前
【Linux】初始Linux:从计算机历史发展、操作系统历史脉络的角度详谈Linux相关的话题,附Linux安装和用户创建(环境准备)详解
linux·运维·服务器·经验分享
努力学习的小廉4 小时前
深入了解linux系统—— 线程池
linux·运维·服务器
大锦终5 小时前
【Linux】进程间通信
linux·运维·服务器·c++
望获linux5 小时前
【实时Linux实战系列】规避缺页中断:mlock/hugetlb 与页面预热
java·linux·服务器·数据库·chrome·算法