高并发服务器-多路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;
}
相关推荐
淮北49427 分钟前
linux系统学习(10.shell基础)
linux·运维·服务器·学习
网硕互联的小客服2 小时前
Windows2008 如何禁用FSO?
运维·服务器·网络·windows·安全
上线就吃代码2 小时前
【等保测评】数据库数据库配置have_ssl参数为yes
服务器·数据库·ssl
Pailugou2 小时前
使用socket实现TCP服务端
服务器·网络·tcp/ip
HalvmånEver3 小时前
Linux:基础开发工具(一)
linux·运维·服务器·开发语言·学习·进阶学习
大地的一角9 小时前
(Linux)ELF格式与库的链接原理
linux·运维·服务器
z202305089 小时前
Linux之中断子系统-内核中断注册源码分析(4)
linux·运维·服务器
Sunlightʊə11 小时前
2.登录页测试用例
运维·服务器·前端·功能测试·单元测试
利刃大大12 小时前
【高并发服务器:HTTP应用】十六、HttpContext上下文模块 && HttpServer服务器模块&& 服务器测试
运维·服务器·http·高并发·项目
wanhengidc13 小时前
云手机通常使用什么架构
服务器·网络·安全·游戏·智能手机·云计算