I/0多路转接之select

I/0多路转接之select

1.select作用

系统提供select函数来实现多路复用输入/输出模型:

  • select调用是用来让我们的程序监视多个文件描述符状态变化的
  • 程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变

2.select函数原型

  • int select(int nfds,fd_set* readfds,fd_set* exceptfds,struct timeval* timeout);
  • nfds:取值为最大文件描述符的数值+1,作用是控制select的轮询监控范围
  • resdfds:读事件集合
  • writefds:写事件集合
  • exceptfds:异常事件集合
  • timeout:阻塞方式传递NULL,非阻塞方式传递0,带有超时时间,用来设置select()的等待时间

3.关于fd_set的结构

是一个整数数组,更严格的来说是一个“位图”,使用位图中对应的位来表示要监视的文件描述符:
在这里插入图片描述
在这里插入图片描述

4. 操作fd_set的接口

  • void FD_CLR(int fd, fd_set *set); // 将fd从事件集合set当中去除掉,本质就是将fd对应的比特位置为0
  • int FD_ISSET(int fd, fd_set *set); // 判断fd文件描述符是否在集合set当中,本质上是判断fd对应的比特位是否为0,返回值为0表示fd不在set当中,为1表示在
  • void FD_SET(int fd, fd_set *set); // 设置文件描述符到set事件集合当中,本质上是将fd对应的比特位置为1
  • void FD_ZERO(fd_set *set); // 用来清空事件集合,本质上是将set中所有比特位置为0

5.select的使用方式

  • select共有三个事件集合:读事件集合,写事件集合,异常事件集合
  • 当需要关注来个文件描述符的某个事件,则将某个文件描述符添加到对应的事件集合当中
    例如:关注0号文件描述符的读事件,则将0号文件描述符添加到读事件集合当中readfds
  • 如果不关注某种事件,则给select传递参数的时候,传递NULL

6.select的返回值

  • 返回值为就绪的文件描述符的个数
  • 就绪的文件描述符存储在事件集合当中返回给调用者
    注意:select会将未就绪的文件描述符从事件集合当中去除掉,因此再次监控时需要重新添加

7.select测试

#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(){
	int listen_sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(listen_sockfd<0){
		perror("socket");
		return 0;
	}
	
	struct sockaddr_in addr;
	addr.sin_familt=AF_INET;
	addr.sin_port=htons(29090);
	addr.sin_addr.s_addr=inet_addr("0.0.0.0");
	int ret=bind(listem_sockfd,(struct sockaddr*)&addr,sizeof(addr));
	if(ret<0){
		perror("bind");
		return 0;
	}
	
	listen(listen_sockfd,5);
	
	fd_set readfds;
	FD_ZERO(&readfds);
  FD_SET(0, &readfds);
  FD_SET(listen_sockfd, &readfds);
	
	while(1){
		fd_set tmp=readfds;
		int ret=select(listen_sockfd+1&tmp,NULL,NULL,NULL);
		printf("ret:%d\n",ret);
		
		if(FD_ISSET(0,&tmp)){
			char buf[1024]={0};
			read(0,buf,sizeof(buf)-1);
			printf("buf:%s",buf);	
		}else{
			printf("0 is not in readfds\n");
		}
		
		if(FD_ISSET(listen_sockfd,&tmp)){
			printf("listen_sockfd read\n");
			accept(listen_sockfd,NULL,NULL);
		}else{
			printf("listen_sockfd not in readfds\n");
		}
	}
	return 0;
}

8.select_tcp

头文件my_select.hpp:

#pragma once
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <vector>

class SelectSvr{
	public:
		SelectSvr(){
			//1.清空事件集合+初始化max_fd
			FD_ZERO(&readfds_);
			max_fd_=-1;	
		}
		~SelectSvr(){}
		void AddFd(int fd){
			//添加并更新最大文件描述符
			FD_SET(fd,&readfds_);
			if(fd>max_fd_){
				max_fd_=fd;	
			}
		}
		void DeleteFd(int fd){
			//移除文件描述符并更新
			FD_CLR(fd,&readfds_);
			for(int i=max_fd_;i>==0;i--){
				if(FD_ISSET(i,&readfds_)){
					max_fd_=i;
					break;	
				}	
			}	
		}
		int Select(std::vetcor<int>* vec){
			int ret=-1;
			while(1){
				fd_set tmp=readfds_;
				ret=select(max_fd_+1,&tmp,NULL,NULL,NULL);
				if(ret<0){
					return ret;
				}else if(ret==0){
					continue;
				}
				for(int i=0;i<max_fd_;i++){
					if(FD_ISSET(i,&tmp)){
						vec->push_back(i);
					}	
				}
				break;
			}
			return ret;
		}
		private:
			fd_set readfds_;
			int msx_fd_;
};

main.cpp:

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "my_select.hpp"

/*
 * 1.tcp的初始化工作
 * 2.select监控
 * 3.依照监控进行处理
 *    listen_sock
 *    new_sockfd
 * */

int main(){
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock < 0){
        perror("socket");
        return 0;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port  = htons(39090);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }

    listen(listen_sock, 5);

    SelectSvr ss;
    ss.AddFd(listen_sock);

    while(1){
        std::vector<int> vec;
        int ret = ss.Select(&vec);
        if(ret < 0){
            continue;
        }

        for(size_t i = 0; i < vec.size(); i++){
            if(listen_sock == vec[i]){
                //侦听套接字
                struct sockaddr_in peer_addr;
                socklen_t peer_addr_len = sizeof(peer_addr);
                int new_sockfd = accept(listen_sock, (struct sockaddr*)&peer_addr, &peer_addr_len);
                if(new_sockfd < 0){
                    continue;
                }

                ss.AddFd(new_sockfd);
                printf("recv new link, ip : %s, port : %d\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
            }else{
               // 新连接套接字有数据到来了
               
               char buf[1024] = {0};
               ssize_t recv_size = recv(vec[i], buf, sizeof(buf) - 1,  0);
               if(recv_size < 0){
                   continue;
               }else if(recv_size == 0){
                   ss.DeleteFd(vec[i]);
                   close(vec[i]);
               }else{
                    printf("[%d sockfd] %s\n", vec[i], buf);
               }

            }
        }

    }



    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值