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参数简介:

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启动被占用
3lfd
4cfd1
5cfd2
6cfd3

readfds,writefds:

​ 当客户端发送请求时,lfd需要读这个请求。同理,有时也会给客户端写数据。 fd_set中的set的意思是集合。这两个是传入传出参数,比如writefds有三个{4,5,6},但是如果在监听时只有两个满足写事件,其就会传出{4,5}。

​ select的返回值是指在监听的时候满足时间的个数。

​ 传入和传出的参数可以理解为一个位图:

传入:监控写文件描述符{3,5,6}

012345678
000101100

传出:监控写文件描述符{5,6}

012345678
000001100

2、select相关参数分析

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);//判断一个文件描述字是否在监听集合中
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转接思路

思路伪代码:

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();  
        }
	}
}

代码:

#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. 优点:跨平台
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值