TcpServer
类在 muduo 网络库中扮演着对外提供服务的重要角色,它封装了 TCP 服务器的基本功能,包括监听连接、处理新连接、管理连接生命周期等。本文将详细剖析 TcpServer
类的代码实现,深入探讨其功能和实现逻辑,并结合 C++ 语言特性进行讲解。
#pragma once
#include "EventLoop.h"
#include "Acceptor.h"
#include "InetAddress.h"
#include "NonCopyable.h"
#include "EventLoopThreadPool.h"
#include "Callbacks.h"
#include "TcpConnection.h"
#include "Buffer.h"
#include
#include
#include
#include
#include
// 对外的服务器编程使用的类
class TcpServer:NonCopyable
{
public:
using ThreadInitCallback = std::function;
enum Option
{
kNoReusePort,
kReusePort,
};
TcpServer(EventLoop *loop,
const InetAddress &listenAddr,
const std::string &nameArg,
Option option = kNoReusePort);
~TcpServer();
void setThreadInitCallback(const ThreadInitCallback &cb) { threadInitCallback_ = cb; }
void setConnectionCallback(const ConnectionCallback &cb) { connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback &cb) { messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback &cb) { writeCompleteCallback_ = cb; }
void setThreadNum(int numThreads);
void start();
private:
void newConnection(int sockfd, const InetAddress &peerAddr);
void removeConnection(const TcpConnectionPtr &conn);
void removeConnectionInLoop(const TcpConnectionPtr &conn);
using ConnectionMap = std::unordered_map;
EventLoop *loop_;
const std::string ipPort_;
const std::string name_;
std::unique_ptr acceptor_;
std::shared_ptr threadPool_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
ThreadInitCallback threadInitCallback_;
int numThreads_;
std::atomic_int started_;
int nextConnId_;
ConnectionMap connections_;
};
头文件包含了一系列必要的头文件,这些头文件定义了 TcpServer
类所依赖的其他类和工具,如 EventLoop
、Acceptor
、InetAddress
等。
using ThreadInitCallback = std::function;
:定义了一个类型别名 ThreadInitCallback
,它是一个函数对象,接受一个 EventLoop*
类型的参数,用于线程初始化回调。enum Option
:定义了一个枚举类型 Option
,包含两个值 kNoReusePort
和 kReusePort
,用于指定是否重用端口。TcpServer(EventLoop *loop, const InetAddress &listenAddr, const std::string &nameArg, Option option = kNoReusePort);
:构造函数,接受一个 EventLoop
指针、一个 InetAddress
对象、一个服务器名称和一个端口重用选项。~TcpServer();
:析构函数,负责清理资源。提供了一系列的回调函数设置方法,如 setThreadInitCallback
、setConnectionCallback
、setMessageCallback
和 setWriteCompleteCallback
,用于设置线程初始化回调、连接建立回调、消息接收回调和写完成回调。
setThreadNum(int numThreads);
:设置线程池中的线程数量。start();
:启动服务器。newConnection(int sockfd, const InetAddress &peerAddr);
:处理新连接。removeConnection(const TcpConnectionPtr &conn);
:移除连接。removeConnectionInLoop(const TcpConnectionPtr &conn);
:在事件循环中移除连接。EventLoop *loop_;
:主事件循环指针。const std::string ipPort_;
:服务器监听的 IP 地址和端口。const std::string name_;
:服务器名称。std::unique_ptr acceptor_;
:用于接受新连接的 Acceptor
对象。std::shared_ptr threadPool_;
:事件循环线程池。ConnectionCallback connectionCallback_;
、MessageCallback messageCallback_;
、WriteCompleteCallback writeCompleteCallback_;
、ThreadInitCallback threadInitCallback_;
:各种回调函数。int numThreads_;
:线程池中的线程数量。std::atomic_int started_;
:原子变量,用于标记服务器是否已经启动。int nextConnId_;
:下一个连接的 ID。ConnectionMap connections_;
:存储所有连接的映射表。#include "TcpServer.h"
#include "LogStream.h"
#include "TcpConnection.h"
#include
#include
static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
if (loop == nullptr)
{
LOG_ERROR << "mainLoop is null!!!";
exit(-1);
}
return loop;
}
TcpServer::TcpServer(EventLoop *loop,
const InetAddress &listenAddr,
const std::string &nameArg,
Option option)
: loop_(CheckLoopNotNull(loop))
, ipPort_(listenAddr.toIpPort())
, name_(nameArg)
, acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
, threadPool_(new EventLoopThreadPool(loop, name_))
, connectionCallback_()
, messageCallback_()
, nextConnId_(1)
, started_(0)
{
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}
TcpServer::~TcpServer()
{
for(auto &item : connections_)
{
TcpConnectionPtr conn(item.second);
item.second.reset();
conn->getLoop()->runInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
}
void TcpServer::setThreadNum(int numThreads)
{
int numThreads_=numThreads;
threadPool_->setThreadNum(numThreads_);
}
void TcpServer::start()
{
if (started_.fetch_add(1) == 0)
{
threadPool_->start(threadInitCallback_);
loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
}
}
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
EventLoop *ioLoop = threadPool_->getNextLoop();
char buf[64] = {0};
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
std::string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_.c_str() << "] - new connection [" << connName.c_str() << "] from" << peerAddr.toIpPort().c_str();
sockaddr_in local;
::memset(&local, 0, sizeof(local));
socklen_t addrlen = sizeof(local);
if(::getsockname(sockfd, (sockaddr *)&local, &addrlen) < 0)
{
LOG_ERROR << "sockets::getLocalAddr";
}
InetAddress localAddr(local);
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
ioLoop->runInLoop(
std::bind(&TcpConnection::connectEstablished, conn));
}
void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
loop_->runInLoop(
std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_.c_str() << "] - connection " << conn->name().c_str();
connections_.erase(conn->name());
EventLoop *ioLoop = conn->getLoop();
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
CheckLoopNotNull
static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
if (loop == nullptr)
{
LOG_ERROR << "mainLoop is null!!!";
exit(-1);
}
return loop;
}
该函数用于检查传入的 EventLoop
指针是否为空,如果为空则输出错误日志并退出程序,确保主事件循环指针不为空。
TcpServer::TcpServer
TcpServer::TcpServer(EventLoop *loop,
const InetAddress &listenAddr,
const std::string &nameArg,
Option option)
: loop_(CheckLoopNotNull(loop))
, ipPort_(listenAddr.toIpPort())
, name_(nameArg)
, acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
, threadPool_(new EventLoopThreadPool(loop, name_))
, connectionCallback_()
, messageCallback_()
, nextConnId_(1)
, started_(0)
{
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}
Acceptor
对象和 EventLoopThreadPool
对象。std::bind
绑定 TcpServer::newConnection
函数到 Acceptor
的新连接回调函数上,当有新连接到来时,Acceptor
会调用 TcpServer::newConnection
函数进行处理。TcpServer::~TcpServer
TcpServer::~TcpServer()
{
for(auto &item : connections_)
{
TcpConnectionPtr conn(item.second);
item.second.reset();
conn->getLoop()->runInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
}
析构函数遍历所有的连接,将连接从 connections_
映射表中移除,并在相应的事件循环中调用 TcpConnection::connectDestroyed
函数销毁连接。
setThreadNum
函数void TcpServer::setThreadNum(int numThreads)
{
int numThreads_=numThreads;
threadPool_->setThreadNum(numThreads_);
}
该函数用于设置线程池中的线程数量,调用 EventLoopThreadPool
的 setThreadNum
函数进行设置。
start
函数void TcpServer::start()
{
if (started_.fetch_add(1) == 0)
{
threadPool_->start(threadInitCallback_);
loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
}
}
std::atomic_int
类型的 started_
变量确保服务器只启动一次。Acceptor
的 listen
函数开始监听连接。newConnection
函数void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
EventLoop *ioLoop = threadPool_->getNextLoop();
char buf[64] = {0};
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
std::string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_.c_str() << "] - new connection [" << connName.c_str() << "] from" << peerAddr.toIpPort().c_str();
sockaddr_in local;
::memset(&local, 0, sizeof(local));
socklen_t addrlen = sizeof(local);
if(::getsockname(sockfd, (sockaddr *)&local, &addrlen) < 0)
{
LOG_ERROR << "sockets::getLocalAddr";
}
InetAddress localAddr(local);
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
ioLoop->runInLoop(
std::bind(&TcpConnection::connectEstablished, conn));
}
TcpConnection
对象,并将其添加到 connections_
映射表中。TcpConnection
的各种回调函数,包括连接建立回调、消息接收回调、写完成回调和关闭回调。TcpConnection::connectEstablished
函数,标志连接建立完成。removeConnection
函数void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
loop_->runInLoop(
std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
该函数将 removeConnectionInLoop
函数封装到主事件循环中执行,确保线程安全。
removeConnectionInLoop
函数void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_.c_str() << "] - connection " << conn->name().c_str();
connections_.erase(conn->name());
EventLoop *ioLoop = conn->getLoop();
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
connections_
映射表中移除指定的连接。TcpConnection::connectDestroyed
函数销毁连接。TcpServer
采用事件驱动模型,通过 EventLoop
和 Acceptor
监听新连接事件,当有新连接到来时,触发相应的回调函数进行处理。这种模型使得服务器能够高效地处理大量并发连接。
使用 EventLoopThreadPool
管理多个事件循环线程,将新连接分配到不同的线程中处理,提高服务器的并发处理能力。通过设置线程池的线程数量,可以根据实际需求调整服务器的性能。
TcpServer
提供了一系列的回调函数,如连接建立回调、消息接收回调、写完成回调等,用户可以通过设置这些回调函数来实现自己的业务逻辑,提高了代码的灵活性和可扩展性。
std::function
和 std::bind
std::function
是一个通用的多态函数包装器,它可以存储、复制和调用任何可调用对象。std::bind
用于将一个可调用对象和其参数进行绑定,生成一个新的可调用对象。在 TcpServer
中,使用 std::function
定义回调函数类型,使用 std::bind
绑定回调函数,使得代码更加灵活和易于管理。
使用 std::unique_ptr
和 std::shared_ptr
管理动态分配的对象,避免了手动内存管理带来的内存泄漏问题。std::unique_ptr
用于管理 Acceptor
对象,确保其所有权的唯一性;std::shared_ptr
用于管理 EventLoopThreadPool
对象,方便在多个地方共享该对象。
使用 std::atomic_int
类型的 started_
变量确保服务器只启动一次,避免了多线程环境下的竞态条件。std::atomic_int
提供了原子操作,保证了变量的读写操作是线程安全的。
TcpServer
类是 muduo 网络库中一个重要的组件,它封装了 TCP 服务器的基本功能,通过事件驱动模型、线程池设计和回调函数机制,实现了高效、灵活和可扩展的服务器编程。同时,代码中充分运用了 C++ 语言的特性,如 std::function
、std::bind
、智能指针和原子操作等,提高了代码的安全性和可维护性。通过深入学习 TcpServer
类的实现,我们可以更好地理解网络编程的原理和 C++ 语言的高级特性。