目录
createNonblocking():创建socket套接字函数,创建一个非阻塞的IO
muduo库是一个Multiple Reactors模型,多反应堆模型。
通过setThreadNumber来设置底层的线程数量,一个IO线程(mainReactor)和多个工作线程(subReactor)
Acceptor的作用就是处理accept,监听新用户连接,新用户连接响应以后,拿到和客户端通信的clientfd打包成Channel,然后根据muduo的轮询算法找到一个subloop,并把Channel给到这个subloop,在这之前需要唤醒这个subloop(通过wakeupfd_:每个loop都有一个wakeupfd_,由Linux系统调用的evenfd创建的,一个带有notify,即带有通知机制的fd,通过写读操作通知)通知机制的fd),mainloop可以向subloop随便写一个整数,唤醒subloop,将打包好的Channel扔给subloop,也就是注册到Poller上!
Acceptor成员变量:
Acceptor的底层是一个listenfd(封装的acceptSocket_),然后这个listenfd打包成Channel放在一个Poller上监听有没有新的用户连接(acceptChannel_)最终返回发生的事件。
- loop_:就是baseloop;
- acceptSocket_:绑定的就是一个listenfd,封装成了Socket;
- acceptChannel_: listenfd(封装成Channel)也需要一个Poller来监听读写事件的;
- newConnectionCallback_:作用: Acceptor给你返回一个connectfd,也就是说有新用户连接成功了,接下来,TcpSever通过轮询选择一个subloop唤醒,然后将connectfd打包成Channel给subloop。
Acceptor成员函数:
Acceptor::Acceptor
构造函数中会创建一个非阻塞的fd ① ,创建一个由这个fd创建的Channel ②,设置套接字选项 ③ ,然后给这个Channel绑定一个handleRead回调函数 ④ 。
createNonblocking():创建socket套接字函数,创建一个非阻塞的IO
SOCK_NONBLOCK: 在非阻塞模式下,当执行读取或写入操作时,如果没有数据可用或无法立即写入数据,函数会立即返回,并不会阻塞线程。这使得程序可以继续执行其他任务或处理其他事件,而不必等待套接字操作完成。
SOCK_CLOEXEC: 当执行exec系统掉用时,操作系统会自动关闭该套接字,确保不会被继承到新的线程中,比如说避免子进程继承父进程的fd,这样可以避免在新程序中不再使用的套接字持续占用资源,并且可以简化代码中资源管理。
Acceptor::~Acceptor()
Acceptor::listen()
启动 Socket的listen函数开始监听;
将该Channel更新为可读事件,交给poller去监听。
Acceptor::handleRead()
①:调用Socket中的accept函数接收新连接,返回connfd;
②:有新的连接之后去执行newConnectionCallback_回调函数,该回调函数由TcpServer设置。
Acceptor.h
#pragma once
#include "noncopyable.h"
#include "Socket.h"
#include "Channel.h"
#include "InetAddress.h"
#include <functional>
class EventLoop;
class InetAddress;
class Acceptor
{
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_ = std::move(cb);
}
bool listenning() const { return listenning_; }
void listen();
private:
void handleRead();
EventLoop *loop_;
Socket acceptSocket_;
Channel acceptChannel_;
NewConnectionCallback newConnectionCallback_;
bool listenning_;
};
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);//SOCK_STREAM:TCP套接字
if (sockfd < 0)
{
LOG_FATAL("%s:%s:%d listen socket create err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno);
}
return sockfd;//改
}
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();//listen
acceptChannel_.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)
{
LOG_ERROR("%s:%s:%d sockfd reached limit! \n", __FILE__, __FUNCTION__, __LINE__);
}
}
}