高并发服务器-多路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;
}
相关推荐
半夏知半秋12 小时前
skynet-socket.lua源码分析
服务器·开发语言·学习·架构·lua
swaveye906015 小时前
轻量服务器创建mysql,并配置远程连接
服务器·mysql·adb
A-刘晨阳16 小时前
Linux安装centos8及基础配置
linux·运维·服务器·操作系统·centos8
Saniffer_SH16 小时前
【高清视频】CXL 2.0 over Fibre演示和答疑 - 将内存拉到服务器10米之外
运维·服务器·网络·人工智能·驱动开发·计算机外设·硬件工程
紫钺-高山仰止16 小时前
【Pyzmq】python 跨进程线程通信 跨平台跨服务器通信
服务器·python·github
saber_andlibert18 小时前
【Linux】IPC——命名管道(fifo)
linux·运维·服务器
TU^18 小时前
Linux--权限
linux·服务器
尘埃不入你眼眸18 小时前
服务器安装Java与nginx与nacos
java·服务器·nginx
The star"'18 小时前
iSCSI服务器
运维·服务器
无垠的广袤18 小时前
【LattePanda Mu 开发套件】AI 图像识别网页服务器
服务器·人工智能·python·单片机·嵌入式硬件·物联网