当前位置:网站首页>I/0多路转接之select
I/0多路转接之select
2022-06-21 17:09:00 【且行且思66】
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;
}
边栏推荐
- 信创环境下缓存服务Redis集群部署
- R language bug? report errors? As for the outcome of sub variables 0 and 1, the original content of the outcome variable is changed through the process of factor and numeric?
- TypeScript的一些基本特征
- Byte Jump propose un nouveau type de réseau léger et efficace, mobovit, qui surpasse GhostNet et mobilenetv3 dans la classification, la détection et d'autres tâches CV!
- epoll+threadpool高并发网络IO模型的实现
- 剑指Offer 56. 删除链表的重复节点
- C语言__attribute__(packed)属性(学习一下)
- Collaborative filtering
- Node模块管理描述文件
- 力扣295. 数据流的中位数
猜你喜欢

Generic type checking for typescript

Byte traffic business experience: realize as soon as possible, sew money bags, and sell all in goods

字節跳動提出輕量級高效新型網絡MoCoViT,在分類、檢測等CV任務上性能優於GhostNet、MobileNetV3!

Start! Alibaba programming summer 2022

网络爬虫开发工具:Screaming Frog SEO Spider

SVG+Canvas粒子动态效果

Markdown writing software: Ulysses v27

雷军的千亿失误?

Redis配置与优化

Day19QPushButton的使用2021-10-30
随机推荐
7.去空格函数-strip
Disclose the design idea of MVVM framework supporting Baidu search, feed and applet, and San core personnel have made great efforts to build it
With mitmdump, don't throw it away, Charles
JZ59.按之字型顺序打印二叉树
剑指 Offer 37. 序列化二叉树
Typescript object type
WXML模板语法、WXSS模板样式、全局配置、页面配置和网络数据请求
How typescript is constructed
RMB 18billion, a super master fund was born in Suzhou
Typescript interface
SVG+Canvas粒子动态效果
Character processing of node
Day21Qt鼠标事件2021-11-01
基于mitmproxy的录制回放接口测试工具
9.后缀、前缀函数-suffix basename addsuffix addprefix
Stata quick check backup (personal notes)
Typescript的通用类型检查
epoll+threadpool高并发网络IO模型的实现
Global installation of node
MySQL common interview questions