【Muduo】接收新连接的Acceptor类

在 Muduo 的设计中,Acceptor类扮演着接受客户端连接请求的关键角色,其运行在每一个TcpServer实例中。一个服务器通常只创建一个监听socket描述字,故其在TcpServer中只有一个,用来监听服务器的唯一socket。它也会将传来的mainLoop和acceptSocket封装成一个Channel,这个Channel只对读事件感兴趣,也就是说,只通过回调函数 handleRead() 处理有新客户端连接请求的事件。

成员变量

  • EventLoop* loop_:指向EventLoop的指针,用于处理 I/O 事件。这个loop是mainloop,负责监听服务器的socket,并且接受客户端的连接请求。
  • Channel acceptChannel_:用于监听服务器 socket 的可读事件(即新的连接请求)。Channel 是 Muduo 中的一个重要类,它封装了文件描述符(file descriptor)和与其相关的事件监听和处理逻辑。
  • bool listenning_:表示是否正在监听新的连接请求。
  • Socket acceptSocket_:Acceptor 会创建一个监听套接字,并绑定到一个特定的 IP 地址和端口上。这个套接字被设置为非阻塞模式,并使用 listen() 函数开始监听连接请求
  • std::function<void(int)> newConnectionCallback_:当接受到新的连接时调用的回调函数。这允许用户自定义如何处理新的连接。

成员函数

  • 构造函数:接收TcpServer传来的mainLoop指针,初始化成员变量,设置监听地址和端口,创建 socket,并将 socket 绑定到指定的地址和端口。
  • listen()Acceptor 会创建一个监听套接字,并绑定到一个特定的 IP 地址和端口上。这个套接字被设置为非阻塞模式,并使用 listen() 函数开始监听连接请求。调用Socket类中封装的操作系统的相关函数(如 ::listen() )来监听 socket 的可读事件。
  • handleRead():处理可读事件(即新的连接请求)的回调函数。当 EventLoop检测到 socket 上有可读事件时,它会调用这个函数。在这个函数中,Acceptor会接受新的连接,并调用用户提供的回调函数 newConnectionCallback_
  • setNewConnectionCallback():设置处理新连接的回调函数。Acceptor 需要一个回调函数来处理新的连接请求。当有新的连接请求到达时,Acceptor 会调用这个回调函数,并传入一个已连接的套接字(通常是 TCPSocket 的实例)。
  • 其他辅助函数:如关闭 socket、检查是否正在监听等。

作用

Acceptor类的主要作用是监听服务器 socket 上的连接请求,并在接受到新的连接时调用TcpServer的 newConnection 回调函数。它是 Muduo 网络库中的一个重要组件,负责处理与客户端建立连接的过程。通过 Acceptor,服务器可以持续监听来自客户端的连接请求,并在需要时接受新的连接。这使得服务器能够同时处理多个客户端的连接请求,从而实现高并发的网络通信。

源码

Acceptor.h
#pragma once

#include "noncopyable.h"
#include "Socket.h"
#include "Channel.h"

#include <functional>

class EventLoop;
class InetAddress;

// Acceptor用的就是用户定义的那个baseloop,也即mainLoop
class Acceptor : noncopyable
{
public:
    using NewConnectionCallback = std::function<void(int sockfd, const InetAddress &)>;
    Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reusePort);
    ~Acceptor();

    // TcpServer的构造函数中设置的TcpServer::newConnection方法
    void setNewConnectionCallback(const NewConnectionCallback &cb)
    {
        newConnectionCallback_ = std::move(cb);
    }

    void listen();

    bool listenning() const { return listenning_; }

private:
    void handleRead();

    EventLoop *loop_; // Acceptor用的就是用户定义的那个baseloop,也即mainLoop
    Socket acceptSocket_;
    Channel acceptChannel_;
    NewConnectionCallback newConnectionCallback_; // 在TcpServer::TcpServer()中设置,将新连接打包
    bool listenning_;
};
Acceptor.cc
#include "Acceptor.h"
#include "InetAddress.h"
#include "LogStream.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

// 创建一个非阻塞的sockfd
static int createNonblocking()
{
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); // SOCK_CLOEXEC标志子进程不接受父进程
    if (sockfd < 0)
    {
        LOG_FATAL << "sockets::createNonblockingOrDie error, errno:" << errno;
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reusePort)
    : loop_(loop)
    , acceptSocket_(createNonblocking()) // 创建套接字socket
    , acceptChannel_(loop, acceptSocket_.fd())
    , listenning_(false)
{
    acceptSocket_.setReuseAddr(true); // 设置tcp选项
    acceptSocket_.setReusePort(true);
    acceptSocket_.bindAddress(listenAddr); // bind
    // TcpServer::start()方法中,调用Acceptor.listen()
    // baseLoop =》 acceptChannel(listendfd)
    // 有新用户连接,要执行一个回调,connfd->channel->subloop
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this)); // acceptChannel_只关心读事件
}

Acceptor::~Acceptor()
{
    acceptChannel_.disableAll(); // 取消fd所有的事件状态
    acceptChannel_.remove();
}

void Acceptor::listen()
{
    LOG_DEBUG << "Acceptor::listen()";
    listenning_ = true;
    acceptSocket_.listen();
    acceptChannel_.enableReading(); // 将accpetorChannel注册在baseLoop的epoll
}

void Acceptor::handleRead()
{
    InetAddress peerAddr;
    int connfd = acceptSocket_.accept(&peerAddr); // 获取到已连接addr
    LOG_DEBUG << "connfd=" << connfd << "  peerAddr:" << peerAddr.toIpPort();
    if (connfd >= 0)
    {
        if (newConnectionCallback_)
        {
            newConnectionCallback_(connfd, peerAddr);
        }
        else
        { // 来了新连接,但是没有对应的回调函数,即无法为这个客户端服务,则直接关闭
            ::close(connfd);
        }
    }
    else
    {
        LOG_ERROR << "Acceptor::handleRead():acceptSocket_.accept error, errno: " << errno;
        if (errno == EMFILE)
        {
            LOG_ERROR << "Acceptor::handleRead() error, sockfd reached limit!";
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值