重写muduo之Acceptor模块

目录

1、回顾 

2、Acceptor

2.1 Socket

2.1.1 Socket.h

2.1.2 Socket.cc

2.2 Acceptor

2.2.1 Acceptor.h

 2.2.2 Acceptor.cc


1、回顾 

Channel、Poller、EPollPoller、EventLoop相当于Reactor模型中的Reactor反应堆和Demultiplex事件分发器

Thread、EventLoopThread、EventLoopThreadPool:如果只有baseloop的话,这个线程既要负责处理连接请求,还要处理读写事件,效率低,所以muduo库是采用Mutiple Reactors,可以通过setthreadnum设置底层线程的数量(设置的是subloop工作线程的数量)。

以上部分已完成。

2、Acceptor

现在我们看Acceptor
Acceptor就是处理accept,监听新用户的连接,拿到跟客户端通信的clientfd,打包成channel,根据muduo的轮询算法找一个subloop,把subloop唤醒(每一个loop都有一个由系统API eventfd创建的wakeupfd(带有线程间通知notify机制的fd),在subloop的poller中监听的,mainloop通过给wakeupfd写一个整数去唤醒subloop),把接收的channel给subloop。

我们打开TCPServer,处理一下Acceptor,

Acceptor运行在我们的baseloop(mainreactor)里面
​​​​​

2.1 Socket封装fd

Acceptor对socket类进行了封装,先来看一下socket

2.1.1 Socket.h

#pragma once
#include "noncopyable.h"class InetAddress;//封装socket fd
class Socket:noncopyable
{
public:explicit Socket(int sockfd):sockfd_(sockfd){}~Socket();int fd()const{return sockfd_;}void bindAddress(const InetAddress& localaddr);void listen();int accept(InetAddress* peeraddr);void shutdownWrite();void setTcpNoDelay(bool on);//直接发送,数据不进行TCP缓存 void setReuseAddr(bool on);void setReusePort(bool on);void setKeepAlive(bool on);
private:const int sockfd_;
};

2.1.2 Socket.cc

#include "Socket.h"
#include "Logger.h"
#include "InetAddress.h"#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/tcp.h>Socket::~Socket()
{close(sockfd_); // 调用系统的close
}void Socket::bindAddress(const InetAddress &localaddr)
{if (0 != bind(sockfd_, (sockaddr *)localaddr.getSockAddr(), sizeof(sockaddr_in))){LOG_FATAL("bind scokfd:%d fail\n", sockfd_);}
}
void Socket::listen()
{if (0 != ::listen(sockfd_, 1024)){LOG_FATAL("listen sockfd:%d fail\n", sockfd_);}
}int Socket::accept(InetAddress *peeraddr)
{/*** 1. accept函数的参数不合法* 2. 对返回的connfd没有设置非阻塞* Reactor模型 one loop per thread* poller + non-blocking IO*/sockaddr_in addr;socklen_t len;bzero(&addr, sizeof addr);int connfd = ::accept(sockfd_, (sockaddr *)&addr, &len);if (connfd >= 0){peeraddr->setSockAddr(addr);}return connfd;
}void Socket::shutdownWrite()
{if (::shutdown(sockfd_, SHUT_WR) < 0){LOG_ERROR("shutdownWrite error");}
}void Socket::setTcpNoDelay(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof optval); // IPPROTO_TCP表示协议级别
}void Socket::setReuseAddr(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof optval); // SQL_SOCKET表示socket级别
}
void Socket::setReusePort(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof optval);
}
void Socket::setKeepAlive(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval);
}

2.2 Acceptor

Acceptor相当于对accept的一个封装,Acceptor是运行在baseloop中的

一条新连接的回调:拿到跟客户端通信的clientfd,打包成channel,根据muduo的轮询算法找一个subloop,把subloop唤醒(getnextloop函数实现)把接收的channel分发给subloop,去监听已连接用户的读写事件。

2.2.1 Acceptor.h

#pragma once
#include "noncopyable.h"
#include "Socket.h"
#include "Channel.h"#include <functional>class EventLoop;
class InetAddress;class Acceptor:noncopyable
{
public:using NewConnectionCallback=std::function<void(int sockfd,const InetAddress&)>;Acceptor(EventLoop* loop,const InetAddress& listenAddr,bool reuseport);~Acceptor();void setNewConnectionCallback(const NewConnectionCallback& cb){NewConnectionCallback_=cb;}bool listenning() const{return listenning_;}void listen();
private:void handleRead();EventLoop* loop_;//Acceptor用的就是用户定义的哪个baseLoop,也称为mainLoopSocket acceptSocket_;Channel acceptChannel_;NewConnectionCallback NewConnectionCallback_;bool listenning_;
};

 2.2.2 Acceptor.cc

#include "Acceptor.h"
#include "Logger.h"
#include "InetAddress.h"#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>static int createNonblocking()//创建非阻塞的I/O 
{int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);if (sockfd < 0){LOG_FATAL("%s:%s:%d listen socket create err:%d\n", __FILE__, __FUNCTION__, __LINE__, errno);}
}Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport):loop_(loop),acceptSocket_(createNonblocking())//创建socket,acceptChannel_(loop,acceptSocket_.fd())//channel和poller都是通过请求本线程的loop和poller通信 ,listenning_(false)
{acceptSocket_.setReuseAddr(true);acceptSocket_.setReusePort(true);acceptSocket_.bindAddress(listenAddr);//bind绑定套接字 //TcpServer::start() Acceptor.listen  如果有新用户的连接,就要执行一个回调(connfd=》打包成channel=》唤醒subloop)//baseLoop => acceptChannel_(listenfd)有事件发生 => 底层反应堆调用回调 acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead,this));//绑定回调 
}
Acceptor::~Acceptor()
{acceptChannel_.disableAll();acceptChannel_.remove();
}void Acceptor::listen()
{listenning_=true;acceptSocket_.listen();//listenacceptChannel_.enableReading();//acceptChannel_=>Poller
}//listenfd有事件发生,就是有新用户连接了
void Acceptor::handleRead()
{InetAddress peerAddr;int connfd=acceptSocket_.accept(&peerAddr);if(connfd>=0){if(NewConnectionCallback_){NewConnectionCallback_(connfd,peerAddr);//轮询找到subloop,唤醒,分发当前的新客户端的channel}else//客户端没有办法去服务 {::close(connfd);}}else{LOG_ERROR("%s:%s:%d accept err:%d\n", __FILE__, __FUNCTION__, __LINE__, errno);if(errno==EMFILE)//EMFILE错误的解决方法:1、调整当前进程文件描述符的上限 2、说明单台服务器不足以支撑现有的流量,进行集群和分布式部署{LOG_ERROR("%s:%s:%d sockfd reached limit\n", __FILE__, __FUNCTION__, __LINE__);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/681424.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【linux】——日志分析

1. 日志文件 1.1 日志文件的分类 日志文件&#xff1a; 是用于记录Linux系统中各种运行消息的文件&#xff0c;相当于Linux主机的“日记". 日志文件对于诊断和解决系统中的问题很有帮助&#xff0c;系统一旦出现问题时及时分析日志就会“有据可查”。此外。当主机遭受攻…

程序员的实用神器,16款程序员生产力工具推荐

前言 在软件开发的海洋中&#xff0c;程序员的实用神器如同航海中的指南针&#xff0c;帮助他们导航、加速开发、优化代码质量&#xff0c;并最终抵达成功的彼岸。这些工具覆盖了从代码编写、版本控制到测试和部署的各个环节。 一、程序员开发工具 Intellij IDEA IntelliJ I…

深入入IAEA底层LinkedList

✅作者简介&#xff1a;大家好&#xff0c;我是再无B&#xff5e;U&#xff5e;G&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1.掌握LinkedList 2.…

Linux|进程控制

进程创建 fork函数初识 在linux中fork函数时非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 返回值&#xff1a;子进程中返回0&#xff0c;父进程返回子进程id&#xff0c;出错返回-1 进程调用fork&#xff0c;当…

hal库定时器中断的使用

本次实验定时器3&#xff1b;验证定时器中断的回调函数功能&#xff1b; 验证方法&#xff1a; 1&#xff09;cubemx配置定时器3和串口2&#xff1b; 2&#xff09;定时器3 预分频720&#xff0c;所以72MHZ进行720分频后&#xff0c;频率为100KHZ&#xff1b;即1秒计数100000次…

Vue从入门到实战Day01

一、Vue快速上手 1. vue概念 概念&#xff1a;Vue是一个用于 构建用户界面的 渐进式 框架 构建用户界面&#xff1a;基于数据动态渲染页面渐进式&#xff1a;循序渐进的学习框架&#xff1a;一套完整的项目解决方案&#xff0c;提升开发效率 优点&#xff1a;大大提升开发效…

linux内核网络源码--通知链

内核的很多子系统之间有很强的依赖性&#xff0c;其中一个子系统侦测到或者产生的事件&#xff0c;其他子系统可能都有兴趣&#xff0c;为了实现这种交互需求&#xff0c;linux使用了所谓的通知链。 本章我们将看到 通知链如何声明以及网络代码定义了哪些链 内核子系统如何向通…

基于Springboot的校园生活服务平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园生活服务平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

如何优化工服识别算法的漏报与误报问题

背景 在一些行业&#xff0c;例如工厂、建筑工地、医院等&#xff0c;员工通常需要穿着特定的工服&#xff0c;工服有助于识别员工、保护员工免受潜在危险以及维护生产环境的清洁度。因此&#xff0c;开发工服识别算法并运用在未穿工服检测系统具有重要的实际意义。 尽管工服…

【AI知识】Stable diffusion常用提示词分享

模型&#xff08;Model&#xff09; majicmixRealistic_v7 majicmixRealistic&#xff08;麦橘写实&#xff09;是融合了多种展现日常生活人物形象的写实风格模型&#xff0c;人物的外观更加接近现实生活&#xff0c;对于光影、皮肤、人物动态均有较好的表现&#xff0c;非常…

PLC数据采集网关的功能和特点-天拓四方

一、引言 随着工业自动化程度的不断提高&#xff0c;数据在生产线上的作用愈发重要。PLC作为工业自动化的核心设备&#xff0c;其数据采集和处理能力直接影响到整个生产线的效率和稳定性。而PLC数据采集网关&#xff0c;作为连接PLC与外部系统的桥梁&#xff0c;正日益受到人们…

Photoshop 2022 for Mac/win:释放创意,打造专业级的图像编辑体验

在数字图像编辑的世界里&#xff0c;Adobe Photoshop 2022无疑是那颗璀璨的明星。这款专为Mac和Windows用户设计的图像处理软件&#xff0c;以其卓越的性能和丰富的功能&#xff0c;赢得了全球数百万创作者的青睐。 Photoshop 2022在继承前代版本强大功能的基础上&#xff0c;…