Muduo网络库解析--网络模块(1)

文章目录

  • 前文
  • InetAddress
      • InetAddress.h
  • Socket
      • Socket.h
      • Socket.cc
  • Acceptor
      • Acceptor.h
      • Acceptor.cc

前文

重写Muduo库实现核心模块的Git仓库

注:本文将重点剖析 Muduo 网络库的核心框架,深入探讨作者精妙的代码设计思路,并针对核心代码部分进行重写,将原本依赖 boost 的实现替换为原生的 C++11 语法。需要说明的是,本文并不打算对整个 Muduo 库进行完整的重写。Muduo库源码链接

在前几篇博客中,我们已经详细讲解了以下模块:

  1. 事件循环相关模块
  • EventLoop:事件循环的核心模块,负责事件监听和分发。
  • Channel:将文件描述符与事件处理回调绑定的桥梁。
  • Poller:事件分发器的抽象基类。
  • EpollPoller:基于 epoll 的事件分发器具体实现。
  1. 线程相关模块
  • Thread:对线程的封装,提供简单易用的接口。
  • EventLoopThread:为每个 EventLoop 提供专属线程。
  • EventLoopThreadPool:线程池,管理多个 EventLoop 线程,实现负载均衡。
  1. 基础模块
  • Buffer:高效管理数据收发的缓冲区模块。
  • Timestamp:提供高精度时间戳支持。
  • Logger:日志记录模块,用于调试和运行时信息输出。

这些模块构成了 Muduo 网络库的核心基础,并在高性能网络编程中扮演着不可或缺的角色。

网络模块的解析将分为两部分进行。在本篇文章中,我们将重点讲解以下模块:

  • Socket
  • InetAddress
  • Acceptor

TcpConnectionTcpServer 这两个模块会在后续文章中进行详细解析。这两个模块是 Muduo 网络库的核心所在,对理解其设计至关重要,请大家务必关注后续内容!

InetAddress

InetAddress对IP和Port进行封装,并提供将其转化为string的方法。

类图如下所示:

Muduo网络库解析--网络模块(1)_第1张图片

InetAddress.h

此类理解起来较为容易,故不做讲解。

class InetAddress
{
public:
    InetAddress() = default;
    InetAddress(u_int16_t port,std::string ip = "127.0.0.1");
    explicit InetAddress(const sockaddr_in& addr) : addr_(addr){}
    

    std::string toIp() const;
    std::string toIpPort() const;
    u_int16_t toPort() const;

    const sockaddr* getSockAddr() const { return (sockaddr*)&addr_; }
    void setSockAddr(const sockaddr_in& addr) { addr_ = addr;}
private:
    struct sockaddr_in addr_; 
};

Socket

Socket 对网络套接字进行了封装,提供了创建、管理和操作套接字的接口,并包含了一系列修改套接字选项的方法。此外,它还为网络编程中的常用操作(如绑定、监听、关闭等)提供了简洁易用的封装,从而简化了套接字的使用。

类图如下:

Muduo网络库解析--网络模块(1)_第2张图片

Socket.h

class Socket : noncopyable
{
public:
    explicit Socket(int sockfd) : sockfd_(sockfd) {}

    ~Socket();

    int fd() const { return sockfd_; }
    void bindAddress(const InetAddress& localaddr);    
    void listen();
    int accpet(InetAddress* peeraddr);

    void shutdownWrite();

    void setTcpNoDelay(bool on);
    void setReuseAddr(bool on);
    void setReusePort(bool on);
    void setKeepAlive(bool on);
private:
    const int sockfd_;
};

解析

setTcpNoDelay:禁止Nagle算法

setReuseAddr:允许重用本地地址

setReusePort:允许重用本地端口

setKeepAlive:周期性测试连接是否存货

Socket.cc

Socket::~Socket()
{
    ::close(sockfd_);
}

void Socket::bindAddress(InetAddress const &localaddr)
{
    if( 0 != ::bind(sockfd_, localaddr.getSockAddr(), sizeof(sockaddr)))
    {
        LOG_FATAL("bind sockfd:%d fail", sockfd_);
    }
}

void Socket::listen()
{
    if(0 != ::listen(sockfd_, 1024))
    {
        LOG_FATAL("listen sockfd:%d fail", sockfd_);
    }
}

int Socket::accpet(InetAddress *peeraddr)
{
    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(0 != ::shutdown(sockfd_, SHUT_WR))
    {
        LOG_ERROR("shutdownWrite error ");
    }
}


void Socket::setTcpNoDelay(bool on)
{
  int optval = on ? 1 : 0;
  ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY,
               &optval, static_cast<socklen_t>(sizeof optval));
  // FIXME CHECK
}

void Socket::setReuseAddr(bool on)
{
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,
                &optval, static_cast<socklen_t>(sizeof optval));
  // FIXME CHECK
}

void Socket::setReusePort(bool on)
{
#ifdef SO_REUSEPORT
    int optval = on ? 1 : 0;
    int ret = ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT,
                         &optval, static_cast<socklen_t>(sizeof optval));
    if (ret < 0 && on)
    {
        LOG_ERROR("SO_REUSEPORT failed.");
    }
#else
    if (on)
    {
        LOG_ERROR << "SO_REUSEPORT is not supported.";
    }
#endif
}

void Socket::setKeepAlive(bool on)
{
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,
                &optval, static_cast<socklen_t>(sizeof optval));
    // FIXME CHECK
}

Acceptor

Acceptor是运行在MainLoop里负责监听并接受客户端的连接,并提供设置新连接回调函数的方法setNewConnectionCallback(const NewConnectionCallback& cb),以便客户端连接后可以调用此回调函数。类图如下:

Muduo网络库解析--网络模块(1)_第3张图片

Acceptor.h

class Acceptor : noncopyable
{
public:
    using NewConnectionCallback = std::function<void(int, const InetAddress&)>;

    Acceptor(EventLoop* loop, const InetAddress& ListenAddr, bool reuseport);
    ~Acceptor();

    void setNewConnectionCallback(const NewConnectionCallback& cb)
    {
        NewConnectionCallback_ = std::move(cb);
    }

    bool listening() const { return listening_; }
    void listen();
private:
    void handleRead();

    EventLoop* loop_;
    Socket acceptSocket_;
    Channel acceptChannel;
    NewConnectionCallback NewConnectionCallback_;
    bool listening_;
};

解析

成员变量:

  • loop_MainLoop,主线程中的 EventLoop,负责管理和分发事件。
  • acceptSocket_:封装了监听套接字的 Socket 对象,用于接受新连接。
  • acceptChannel_:与监听套接字关联的 Channel,负责将监听套接字的事件(如新连接到来)分发到注册的回调函数中处理。

成员方法:

  • listen():开启监听套接字
  • handleRead():注册到acceptChannel的读回调函数,当有新连接到来时(读事件),执行此函数

Acceptor.cc

static int CreateNonblocking()
{
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
    if(sockfd < 0)
    {
        LOG_FATAL("%s:%s:%d listen socket create error:%d", __FILE__, __FUNCTION__, __LINE__, errno);
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop *loop, InetAddress const &ListenAddr,
                   bool reuseport) : 
    loop_(loop),
    acceptSocket_(CreateNonblocking()),
    acceptChannel(loop_, acceptSocket_.fd()),
    listening_(false)
{
    acceptSocket_.setReuseAddr(true);
    acceptSocket_.setReusePort(true);
    acceptSocket_.bindAddress(ListenAddr);
    acceptChannel.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

Acceptor::~Acceptor()
{
    acceptChannel.disableAll();
    acceptChannel.remove();
}

void Acceptor::listen()
{
    listening_ = true;
    acceptSocket_.listen();
    acceptChannel.enableReading();
}

void Acceptor::handleRead()
{
    InetAddress peerAddr;
    int connfd = acceptSocket_.accpet(&peerAddr);
    if(connfd >= 0)
    {
        if(NewConnectionCallback_)
        {
            NewConnectionCallback_(connfd, peerAddr);
        }
        else
        {
            ::close(connfd);
        }
    }  
    else
    {
        LOG_ERROR("%s:%s:%d listen socket accept error:%d", __FILE__, __FUNCTION__, __LINE__, errno);
        if(errno == EMFILE)
        {
             LOG_ERROR("%s:%s:%d listen socket limit error:%d", __FILE__, __FUNCTION__, __LINE__, errno);
        }
    }

}

到这里,InetAddressSocketAcceptor这三个模组就讲完了,这三个模块内容不多,也不难,但是是网络模块的基础部分。下一节的TcpServerTcpConnection是本系列的重中之重,请大家记得订阅!

你可能感兴趣的:(剖析Muduo,网络,c++,tcp/ip)