本项目参考的陈硕老师的思想
1. 基础概念
进程里有 Reactor、Acceptor、Handler 这三个对象
- Reactor 对象的作用是监听和分发事件;
- Acceptor 对象的作用是获取连接;
- Handler 对象的作用是处理业务;
先说说 阻塞I/O,非阻塞I/O,异步I/O:
举个你去饭堂吃饭的例子,你好比应用程序,饭堂好比操作系统。
阻塞I/O好比,你去饭堂吃饭,但是饭堂的菜还没做好,然后你就一直在那里等啊等,等了好长一段时间终于等到饭堂阿姨把菜端了出来(数据准备的过程),但是你还得继续等阿姨把菜(内核空间)打到你的饭盒里(用户空间),经历完这两个过程,你才可以离开。
非阻塞I/O好比,你去了饭堂,问阿姨菜做好了没有,阿姨告诉你没,你就离开了,过几十分钟,你又来饭堂问阿姨,阿姨说做好了,于是阿姨帮你把菜打到你的饭盒里,这个过程你是得等待的。
异步I/O好比,你让饭堂阿姨将菜做好并把菜打到饭盒里后,把饭盒送到你面前,整个过程你都不需要任何等待。
很明显,异步I/O比同步I/O性能更好,因为异步I/O在「内核数据准备好」和「数据从内核空间拷贝到用户空间」这两个过程都不用等待。
Proactor正是采用了异步I/O技术,所以被称为异步网络模型。
现在我们再来理解 Reactor 和 Proactor的区别,就比较清晰了。
-
Reactor是非阻塞同步网络模式,感知的是就绪可读写事件。在每次感知到有事件发生(比如可读就绪事件)后,就需要应用进程主动调用read方法来完成数据的读取,也就是要应用进程主动将socket接收缓存中的数据读到应用进程内存中,这个过程是同步的,读取完数据后应用进程才能处理数据。
-
Proactor是异步网络模式,感知的是已完成的读写事件。在发起异步读写请求时,需要传入数据缓冲区的地址(用来存放结果数据)等信息,这样系统内核才可以自动帮我们把数据的读写工作完成,这里的读写工作全程由操作系统来做,并不需要像Reactor那样还需要应用进程主动发起read/write来读写数据,操作系统完成读写工作后,就会通知应用进程直接处理数据。
因此,Reactor可以理解为「来了事件操作系统通知应用程序,让应用进程来处理」,而Proactor可以理解为「带来了事件操作系统来处理,处理完再通知应用进程」。这里的「事件」就是有新连接、有数据可读、有数据可写的这些I/O事件这里的「处理」包含从驱动读取到内核以及从内核读取到用户空间。
举个实际生活中的例子,Reactor模式就是快递员在楼下,给你打电话告诉你快递到你家小区了,你需要自己下楼来拿快递。而在Proactor模式下,快递员直接将快递送到你家门口,然后通知你。
无论是Reactor,还是Proactor,都是一种基于「事件分发」的网络编程模式,区别在于Reactor模式是基于「待完成」的I/O事件,而Proactor模式则是基于「已完成」的I/O事件。
Reactor模型组成部分 :
· Event:事件
· Reactor:反应堆
· Demultiplex:多路事件分发器
· EventHandler:事件处理器
在muduo中的调用关系:
- 将事件及其处理方法注册到Reactor,Reactor中存储了连接套接字connfd以及其感兴趣的事件event
- Reactor向其所对应的Demultiplex注册相应的connfd+事件,启动反应堆
- 当Demultiplex检测到connfd上有事件发生,就会返回相应事件
- Reactor根据事件去调用EventHandler处理程序
muduo核心机制
-
Main Reactor:
- 主线程运行
EventLoop
,通过epoll
监听新连接 (accept
事件)。 - 触发后,调用
Acceptor
的回调,创建TCPConnection
对象。
- 主线程运行
-
SubReactor分配:
- 通过轮询算法 将新连接分配给某个
SubReactor
(子线程的EventLoop
)。 - 每个
SubReactor
独立运行一个EventLoop
,处理连接的读写事件 (如epoll_wait
监听数据)。
- 通过轮询算法 将新连接分配给某个
-
核心设计:
- One Loop Per Thread :每个
EventLoop
绑定一个线程,实现线程级资源隔离,避免锁竞争。 - 分工明确:Main Reactor专注连接建立,SubReactor专注业务处理,提升并发性能。
- One Loop Per Thread :每个
一句话 :muduo用主从Reactor + 线程级事件循环(one loop per thread
),实现高并发、低延迟的网络服务。
1.全流程示例
步骤 | 对应muduo组件 | 核心机制 |
---|---|---|
① 注册事件到Reactor | Acceptor 向Main Reactor 注册listenfd 的EPOLLIN 事件 |
Main Reactor 的EventLoop 通过epoll 监听连接建立事件。 |
② Reactor注册到Demultiplex | EventLoop 调用Poller::epoll_ctl() 注册listenfd |
Main Reactor 启动事件循环(EventLoop::loop() ),调用epoll_wait 阻塞监听。 |
③ Demultiplex检测事件 | Poller::epoll_wait() 返回活跃事件 |
当新连接到达,epoll_wait 唤醒Main Reactor ,触发Acceptor 的回调。 |
④ Reactor调用EventHandler | Acceptor 回调创建TcpConnection ,并分配至SubReactor |
主线程通过轮询算法 将TcpConnection 分配给某个SubReactor 的EventLoop 。 |
muduo中Reactor模式工作流程
在muduo库中,Reactor模式的核心流程如下:
-
注册监听 :
Acceptor
向Main Reactor
(主线程的EventLoop
)注册listenfd
的EPOLLIN
事件,表示监听新连接。Main Reactor
通过Poller
(封装epoll
)调用epoll_ctl
将listenfd
加入监听列表,并启动事件循环(EventLoop::loop()
),在epoll_wait
中阻塞等待事件。 -
接收连接 :
当新连接到达时,
epoll_wait
返回listenfd
的读事件,唤醒Main Reactor
,触发Acceptor
的回调函数。该回调执行accept()
创建TcpConnection
对象,生成客户端连接的connfd
。 -
分配连接 :
主线程通过轮询算法 (如Round-Robin)将
TcpConnection
分配给一个SubReactor
(子线程的EventLoop
)。每个SubReactor
独立运行one loop per thread
模型,即一个EventLoop
绑定一个线程,内部通过epoll_wait
监听已注册连接的读写事件。 -
处理事件 :
TcpConnection
的connfd
被注册到SubReactor
的Poller
中,后续该连接的IO事件(如数据到达)由对应的EventLoop
驱动处理,触发Channel
中用户注册的回调(如onMessage
),实现业务逻辑。
核心机制:
- 主从分工 :
Main Reactor
专注连接建立,SubReactor
专注数据处理。 - 线程隔离 :每个
SubReactor
绑定独立线程,通过one loop per thread
避免锁竞争,提升并发性能。 - 事件驱动 :全程依赖
epoll
监听事件,无忙等轮询,高效响应IO。
一句话 :muduo通过Main Reactor
监听连接、SubReactor
线程级事件循环、epoll
高效分发,实现高并发网络服务。
2.代码
本项目参考的是陈硕老师的muduo网络库
1.文件结构:
muduo网络库/
├── CMakeLists.txt
├── build/
│ ├── example/
│ ├── lib/
│ ├── src/
│ └── ...
├── example/
│ ├── CMakeLists.txt
│ ├── testserver
│ └── testserver.cc
├── include/
│ ├── Acceptor.h
│ ├── Buffer.h
│ ├── Callbacks.h
│ ├── Channel.h
│ ├── CurrentThread.h
│ ├── EPollPoller.h
│ ├── EventLoop.h
│ ├── EventLoopThread.h
│ ├── EventLoopThreadPool.h
│ ├── InetAddress.h
│ ├── Logger.h
│ ├── noncopyable.h
│ ├── Poller.h
│ ├── Socket.h
│ ├── TcpConnection.h
│ ├── TcpServer.h
│ ├── Thread.h
│ └── Timestamp.h
├── lib/
│ └── Debug/
└── src/
├── Acceptor.cc
├── Buffer.cc
├── Channel.cc
├── CMakeLists.txt
├── CurrentThread.cc
├── DefaultPoller.cc
├── EPollPoller.cc
├── EventLoop.cc
├── EventLoopThread.cc
├── EventLoopThreadPool.cc
├── InetAddress.cc
├── Logger.cc
├── Poller.cc
├── Socket.cc
├── TcpConnection.cc
├── TcpServer.cc
├── Thread.cc
└── Timestamp.cc
各个模块作用:
Acceptor:
负责处理新连接的接收。
监听主线程的文件描述符,接收客户端的连接请求,并将新连接分发给 TcpConnection。
TcpConnection:
表示一个具体的 TCP 连接。
负责管理连接的生命周期,包括数据的读写、事件处理以及连接的关闭。
Buffer:
提供高效的缓冲区管理。
用于存储从网络中接收的数据或待发送的数据,支持自动扩容和高效的读写操作。
EventLoop:
事件循环模块,负责事件的轮询和分发。
每个线程运行一个 EventLoop,实现 one loop per thread 模型,处理 I/O 事件、定时器事件等。
Logger:
日志模块,用于记录系统运行时的日志信息。
支持不同的日志级别(如 INFO、DEBUG、ERROR),便于调试和监控。
Channel:
表示一个文件描述符(如 socket)的抽象。
负责管理文件描述符的事件(如可读、可写)以及事件的回调处理。
Poller:
事件多路复用模块,底层使用 epoll 或其他机制实现。
负责监听多个文件描述符的事件,并将事件分发给对应的 Channel。
EventLoopThreadPool:
线程池模块,管理多个 EventLoop。
主线程通过线程池分发任务到子线程的 EventLoop,实现多线程并发处理。
TcpServer:
服务器模块,整合了 Acceptor、TcpConnection、EventLoop 等模块。
提供对外的接口,负责管理所有的连接和事件处理。
InetAddress:
表示网络地址(IP 和端口)的封装。
提供对地址的解析和格式化操作。
2.具体代码
每个部分 先是 .h , 后是 .cc 文件
Acceptor.h
#pragma once
#include <functional>
#include "noncopyable.h"
#include "Socket.h"
#include "Channel.h"
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 也称作mainLoop
Socket acceptSocket_;//专门用于接收新连接的socket
Channel acceptChannel_;//专门用于监听新连接的channel
NewConnectionCallback NewConnectionCallback_;//新连接的回调函数
bool listenning_;//是否在监听
};
Acceptor.cc
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include "Acceptor.h"
#include "Logger.h"
#include "InetAddress.h"
static int createNonblocking()
{
int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_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())
, acceptChannel_(loop, acceptSocket_.fd())
, listenning_(false)
{
acceptSocket_.setReuseAddr(true);
acceptSocket_.setReusePort(true);
acceptSocket_.bindAddress(listenAddr);
// TcpServer::start() => Acceptor.listen() 如果有新用户连接 要执行一个回调(accept => connfd => 打包成Channel => 唤醒subloop)
// baseloop监听到有事件发生 => acceptChannel_(listenfd) => 执行该回调函数
acceptChannel_.setReadCallback(
std::bind(&Acceptor::handleRead, this));
}
Acceptor::~Acceptor()
{
acceptChannel_.disableAll(); // 把从Poller中感兴趣的事件删除掉
acceptChannel_.remove(); // 调用EventLoop->removeChannel => Poller->removeChannel 把Poller的ChannelMap对应的部分删除
}
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__);
}
}
}
Buffer.h
// 提供高效的数据缓冲区,支持读写操作和自动扩容
// 这是一个 应用层网络I/O缓冲区 ,主要用于:
// 接收数据 :从TCP socket读取数据时暂存(可能分多次到达)。
// 发送数据 :累积待发送的数据,批量写入socket(减少系统调用)。
#pragma once
#include <vector>
#include <string>
#include <algorithm>
#include <stddef.h>
// 网络库底层的缓冲区类型定义
class Buffer
{
public:
static const size_t kCheapPrepend = 8;//初始预留的prependabel空间大小
static const size_t kInitialSize = 1024;
explicit Buffer(size_t initalSize = kInitialSize)
: buffer_(kCheapPrepend + initalSize)
, readerIndex_(kCheapPrepend)
, writerIndex_(kCheapPrepend)
{
}
size_t readableBytes() const { return writerIndex_ - readerIndex_; }
size_t writableBytes() const { return buffer_.size() - writerIndex_; }
size_t prependableBytes() const { return readerIndex_; }
// 返回缓冲区中可读数据的起始地址
const char *peek() const { return begin() + readerIndex_; }
void retrieve(size_t len)
{
if (len < readableBytes())
{
readerIndex_ += len; // 说明应用只读取了可读缓冲区数据的一部分,就是len长度
//还剩下readerIndex+=len到writerIndex_的数据未读
}
else // len == readableBytes()
{
retrieveAll();
}
}
void retrieveAll()
{
readerIndex_ = kCheapPrepend;
writerIndex_ = kCheapPrepend;
}
// 把onMessage函数上报的Buffer数据 转成string类型的数据返回
std::string retrieveAllAsString() { return retrieveAsString(readableBytes()); }
std::string retrieveAsString(size_t len)
{
std::string result(peek(), len);
retrieve(len); // 上面一句把缓冲区中可读的数据已经读取出来 这里肯定要对缓冲区进行复位操作
return result;
}
// buffer_.size - writerIndex_
void ensureWritableBytes(size_t len)
{
if (writableBytes() < len)
{
makeSpace(len); // 扩容
}
}
// 把[data, data+len]内存上的数据添加到writable缓冲区当中
void append(const char *data, size_t len)
{
ensureWritableBytes(len);
std::copy(data, data+len, beginWrite());
writerIndex_ += len;
}
char *beginWrite() { return begin() + writerIndex_; }
const char *beginWrite() const { return begin() + writerIndex_; }
// 从fd上读取数据
ssize_t readFd(int fd, int *saveErrno);
// 通过fd发送数据
ssize_t writeFd(int fd, int *saveErrno);
private:
// vector底层数组首元素的地址 也就是数组的起始地址
char *begin() { return &*buffer_.begin(); }
const char *begin() const { return &*buffer_.begin(); }
void makeSpace(size_t len)
{
/**
* | kCheapPrepend |xxx| reader | writer | // xxx标示reader中已读的部分
* | kCheapPrepend | reader | len |
**/
//可写空间 + 已读空间 < 新数据长度 + 预留空间
if (writableBytes() + prependableBytes() < len + kCheapPrepend) // 也就是说 len > xxx前面剩余的空间 + writer的部分
{
buffer_.resize(writerIndex_ + len);
}
else // 这里说明 len <= xxx + writer 把reader搬到从xxx开始 使得xxx后面是一段连续空间
{
size_t readable = readableBytes(); // readable = reader的长度
// 将当前缓冲区中从readerIndex_到writerIndex_的数据
// 拷贝到缓冲区起始位置kCheapPrepend处,以便腾出更多的可写空间
std::copy(begin() + readerIndex_,
begin() + writerIndex_,
begin() + kCheapPrepend);
readerIndex_ = kCheapPrepend;
writerIndex_ = readerIndex_ + readable;
}
}
std::vector<char> buffer_;
size_t readerIndex_;
size_t writerIndex_;
};
Buffer.cc
#include <errno.h>
#include <sys/uio.h>
#include <unistd.h>
#include <cstddef> // for ptrdiff_t
#include "Buffer.h"
/**
* 从fd上读取数据 Poller工作在LT模式
* Buffer缓冲区是有大小的! 但是从fd上读取数据的时候 却不知道tcp数据的最终大小
*
* @description: 从socket读到缓冲区的方法是使用readv先读至buffer_,
* Buffer_空间如果不够会读入到栈上65536个字节大小的空间,然后以append的
* 方式追加入buffer_。既考虑了避免系统调用带来开销,又不影响数据的接收。
**/
ssize_t Buffer::readFd(int fd, int *saveErrno)
{
// 栈额外空间,用于从套接字往出读时,当buffer_暂时不够用时暂存数据,待buffer_重新分配足够空间后,在把数据交换给buffer_。
char extrabuf[65536] = {0}; // 栈上内存空间 65536/1024 = 64KB
/*
struct iovec {
ptr_t iov_base; // iov_base指向的缓冲区存放的是readv所接收的数据或是writev将要发送的数据
size_t iov_len; // iov_len在各种情况下分别确定了接收的最大长度以及实际写入的长度
};
*/
// 使用iovec分配两个连续的缓冲区
struct iovec vec[2];
const size_t writable = writableBytes(); // 这是Buffer底层缓冲区剩余的可写空间大小 不一定能完全存储从fd读出的数据
// 第一块缓冲区,指向可写空间
vec[0].iov_base = begin() + writerIndex_;
vec[0].iov_len = writable;
// 第二块缓冲区,指向栈空间
vec[1].iov_base = extrabuf;
vec[1].iov_len = sizeof(extrabuf);
// when there is enough space in this buffer, don't read into extrabuf.
// when extrabuf is used, we read 128k-1 bytes at most.
// 这里之所以说最多128k-1字节,是因为若writable为64k-1,那么需要两个缓冲区 第一个64k-1 第二个64k 所以做多128k-1
// 如果第一个缓冲区>=64k 那就只采用一个缓冲区 而不使用栈空间extrabuf[65536]的内容
const int iovcnt = (writable < sizeof(extrabuf)) ? 2 : 1;
const ssize_t n = ::readv(fd, vec, iovcnt);
if (n < 0)
{
*saveErrno = errno;
}
else if (n <= writable) // Buffer的可写缓冲区已经够存储读出来的数据了
{
writerIndex_ += n;
}
else // extrabuf里面也写入了n-writable长度的数据
{
writerIndex_ = buffer_.size();
append(extrabuf, n - writable); // 对buffer_扩容 并将extrabuf存储的另一部分数据追加至buffer_
}
return n;
}
// inputBuffer_.readFd表示将对端数据读到inputBuffer_中,移动writerIndex_指针
// outputBuffer_.writeFd标示将数据写入到outputBuffer_中,从readerIndex_开始,可以写readableBytes()个字节
ssize_t Buffer::writeFd(int fd, int *saveErrno)
{
ssize_t n = ::write(fd, peek(), readableBytes());
if (n < 0)
{
*saveErrno = errno;
}
return n;
}
Callbacks.h
#pragma once
#include <memory>
#include <functional>
class Buffer;
class TcpConnection;
class Timestamp;
using TcpConnectionPtr = std::shared_ptr<TcpConnection>;
using ConnectionCallback = std::function<void(const TcpConnectionPtr &)>;
using CloseCallback = std::function<void(const TcpConnectionPtr &)>;
using WriteCompleteCallback = std::function<void(const TcpConnectionPtr &)>;
using HighWaterMarkCallback = std::function<void(const TcpConnectionPtr &, size_t)>;
using MessageCallback = std::function<void(const TcpConnectionPtr &, Buffer *, Timestamp)>;
Channel.h
#pragma once
#include <functional>
#include <memory>
#include "noncopyable.h"
#include "Timestamp.h"
class EventLoop;
/**
* 理清楚 EventLoop、Channel、Poller之间的关系 Reactor模型上对应多路事件分发器
* Channel理解为通道 封装了sockfd和其感兴趣的event 如EPOLLIN、EPOLLOUT事件 还绑定了poller返回的具体事件
**/
class Channel : noncopyable
{
public:
using EventCallback = std::function<void()>; // muduo仍使用typedef
using ReadEventCallback = std::function<void(Timestamp)>;
Channel(EventLoop *loop, int fd);
~Channel();
// fd得到Poller通知以后 处理事件 handleEvent在EventLoop::loop()中调用
void handleEvent(Timestamp receiveTime);
// 设置回调函数对象
void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }
void setWriteCallback(EventCallback cb) { writeCallback_ = std::move(cb); }
void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); }
void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); }
// 防止当channel被手动remove掉 channel还在执行回调操作
void tie(const std::shared_ptr<void> &);
int fd() const { return fd_; }
int events() const { return events_; }
void set_revents(int revt) { revents_ = revt; }
// 设置fd相应的事件状态 相当于epoll_ctl add delete
void enableReading() { events_ |= kReadEvent; update(); }
void disableReading() { events_ &= ~kReadEvent; update(); }
void enableWriting() { events_ |= kWriteEvent; update(); }
void disableWriting() { events_ &= ~kWriteEvent; update(); }
void disableAll() { events_ = kNoneEvent; update(); }
// 返回fd当前的事件状态
bool isNoneEvent() const { return events_ == kNoneEvent; }
bool isWriting() const { return events_ & kWriteEvent; }
bool isReading() const { return events_ & kReadEvent; }
int index() { return index_; }
void set_index(int idx) { index_ = idx; }
// one loop per thread
EventLoop *ownerLoop() { return loop_; }
void remove();
private:
void update();
void handleEventWithGuard(Timestamp receiveTime);
static const int kNoneEvent; // 没有事件
static const int kReadEvent; // 读事件
static const int kWriteEvent; // 写事件
EventLoop *loop_; // 事件循环
const int fd_; // fd,Poller监听的对象
int events_; // 注册fd感兴趣的事件
int revents_; // Poller返回的具体发生的事件
int index_; // 表示channel在poller中的状态
std::weak_ptr<void> tie_;
bool tied_;
// 因为channel通道里可获知fd最终发生的具体的事件events,所以它负责调用具体事件的回调操作
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
};
Channel.cc
#include <sys/epoll.h>
#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"
const int Channel::kNoneEvent = 0; //空事件
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI; //读事件
const int Channel::kWriteEvent = EPOLLOUT; //写事件
// EventLoop: ChannelList Poller
Channel::Channel(EventLoop *loop, int fd)
: loop_(loop)
, fd_(fd)
, events_(0)
, revents_(0)
, index_(-1)
, tied_(false)
{
}
Channel::~Channel()
{
}
// channel的tie方法什么时候调用过? TcpConnection => channel
/**
* TcpConnection中注册了Channel对应的回调函数,传入的回调函数均为TcpConnection
* 对象的成员方法,因此可以说明一点就是:Channel的结束一定晚于TcpConnection对象!
* 此处用tie去解决TcpConnection和Channel的生命周期时长问题,从而保证了Channel对象能够在
* TcpConnection销毁前销毁。
**/
void Channel::tie(const std::shared_ptr<void> &obj)
{
tie_ = obj;
tied_ = true;
}
//update 和remove => EpollPoller 更新channel在poller中的状态
/**
* 当改变channel所表示的fd的events事件后,update负责再poller里面更改fd相应的事件epoll_ctl
**/
void Channel::update()
{
// 通过channel所属的eventloop,调用poller的相应方法,注册fd的events事件
loop_->updateChannel(this);
}
// 在channel所属的EventLoop中把当前的channel删除掉
void Channel::remove()
{
loop_->removeChannel(this);
}
void Channel::handleEvent(Timestamp receiveTime)
{
if (tied_)
{
std::shared_ptr<void> guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
// 如果提升失败了 就不做任何处理 说明Channel的TcpConnection对象已经不存在了
}
else
{
handleEventWithGuard(receiveTime);
}
}
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
LOG_INFO("channel handleEvent revents:%d\n", revents_);
// 关闭
if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) // 当TcpConnection对应Channel 通过shutdown 关闭写端 epoll触发EPOLLHUP
{
if (closeCallback_)
{
closeCallback_();
}
}
// 错误
if (revents_ & EPOLLERR)
{
if (errorCallback_)
{
errorCallback_();
}
}
// 读
if (revents_ & (EPOLLIN | EPOLLPRI))
{
if (readCallback_)
{
readCallback_(receiveTime);
}
}
// 写
if (revents_ & EPOLLOUT)
{
if (writeCallback_)
{
writeCallback_();
}
}
}
CurrentThread.h
#pragma once
#include <unistd.h>
#include <sys/syscall.h>
namespace CurrentThread
{
extern __thread int t_cachedTid; // 保存tid缓存 因为系统调用非常耗时 拿到tid后将其保存
void cacheTid();
inline int tid() // 内联函数只在当前文件中起作用
{
if (__builtin_expect(t_cachedTid == 0, 0)) // __builtin_expect 是一种底层优化 此语句意思是如果还未获取tid 进入if 通过cacheTid()系统调用获取tid
{
cacheTid();
}
return t_cachedTid;
}
}
CurrentThread.cc
#include "CurrentThread.h"
namespace CurrentThread
{
__thread int t_cachedTid = 0;
void cacheTid()
{
if (t_cachedTid == 0)
{
t_cachedTid = static_cast<pid_t>(::syscall(SYS_gettid));
}
}
}
DefalutPoller.cc
#include <stdlib.h>
#include "Poller.h"
#include "EPollPoller.h"
Poller *Poller::newDefaultPoller(EventLoop *loop)
{
if (::getenv("MUDUO_USE_POLL"))
{
return nullptr; // 生成poll的实例
}
else
{
return new EPollPoller(loop); // 生成epoll的实例
}
}
EPollPoller.h
#pragma once
#include <vector>
#include <sys/epoll.h>
#include "Poller.h"
#include "Timestamp.h"
/**
* epoll的使用:
* 1. epoll_create
* 2. epoll_ctl (add, mod, del)
* 3. epoll_wait
**/
class Channel;
class EPollPoller : public Poller
{
public:
EPollPoller(EventLoop *loop);
~EPollPoller() override;
// 重写基类Poller的抽象方法
Timestamp poll(int timeoutMs, ChannelList *activeChannels) override;
void updateChannel(Channel *channel) override;
void removeChannel(Channel *channel) override;
private:
static const int kInitEventListSize = 16;
// 填写活跃的连接
void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
// 更新channel通道 其实就是调用epoll_ctl
void update(int operation, Channel *channel);
using EventList = std::vector<epoll_event>; // C++中可以省略struct 直接写epoll_event即可
int epollfd_; // epoll_create创建返回的fd保存在epollfd_中
EventList events_; // 用于存放epoll_wait返回的所有发生的事件的文件描述符事件集
};
EPollPoller.cc
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include "EPollPoller.h"
#include "Logger.h"
#include "Channel.h"
const int kNew = -1; // 某个channel还没添加至Poller // channel的成员index_初始化为-1
const int kAdded = 1; // 某个channel已经添加至Poller
const int kDeleted = 2; // 某个channel已经从Poller删除
EPollPoller::EPollPoller(EventLoop *loop)
: Poller(loop)
, epollfd_(::epoll_create1(EPOLL_CLOEXEC))
, events_(kInitEventListSize) // vector<epoll_event>(16)
{
if (epollfd_ < 0)
{
LOG_FATAL("epoll_create error:%d \n", errno);
}
}
EPollPoller::~EPollPoller()
{
::close(epollfd_);
}
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
// 由于频繁调用poll 实际上应该用LOG_DEBUG输出日志更为合理 当遇到并发场景 关闭DEBUG日志提升效率
LOG_INFO("func=%s => fd total count:%lu\n", __FUNCTION__, channels_.size());
int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs);
int saveErrno = errno;
Timestamp now(Timestamp::now());
if (numEvents > 0)
{
LOG_INFO("%d events happend\n", numEvents); // LOG_DEBUG最合理
fillActiveChannels(numEvents, activeChannels);
if (numEvents == events_.size()) // 扩容操作
{
events_.resize(events_.size() * 2);
}
}
else if (numEvents == 0)
{
LOG_DEBUG("%s timeout!\n", __FUNCTION__);
}
else
{
if (saveErrno != EINTR)
{
errno = saveErrno;
LOG_ERROR("EPollPoller::poll() error!");
}
}
return now;
}
// channel update remove => EventLoop updateChannel removeChannel => Poller updateChannel removeChannel
void EPollPoller::updateChannel(Channel *channel)
{
const int index = channel->index();
LOG_INFO("func=%s => fd=%d events=%d index=%d\n", __FUNCTION__, channel->fd(), channel->events(), index);
if (index == kNew || index == kDeleted)
{
if (index == kNew)
{
int fd = channel->fd();
channels_[fd] = channel;
}
else // index == kDeleted
{
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD, channel);
}
else // channel已经在Poller中注册过了
{
int fd = channel->fd();
if (channel->isNoneEvent())
{
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted);
}
else
{
update(EPOLL_CTL_MOD, channel);
}
}
}
// 从Poller中删除channel
void EPollPoller::removeChannel(Channel *channel)
{
int fd = channel->fd();
channels_.erase(fd);
LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd);
int index = channel->index();
if (index == kAdded)
{
update(EPOLL_CTL_DEL, channel);
}
channel->set_index(kNew);
}
// 填写活跃的连接
void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const
{
for (int i = 0; i < numEvents; ++i)
{
Channel *channel = static_cast<Channel *>(events_[i].data.ptr);
channel->set_revents(events_[i].events);
activeChannels->push_back(channel); // EventLoop就拿到了它的Poller给它返回的所有发生事件的channel列表了
}
}
// 更新channel通道 其实就是调用epoll_ctl add/mod/del
void EPollPoller::update(int operation, Channel *channel)
{
epoll_event event;
::memset(&event, 0, sizeof(event));
int fd = channel->fd();
event.events = channel->events();
event.data.fd = fd;
event.data.ptr = channel;
if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
{
if (operation == EPOLL_CTL_DEL)
{
LOG_ERROR("epoll_ctl del error:%d\n", errno);
}
else
{
LOG_FATAL("epoll_ctl add/mod error:%d\n", errno);
}
}
}
EventLoop.h
#pragma once
#include <functional>
#include <vector>
#include <atomic>
#include <memory>
#include <mutex>
#include "noncopyable.h"
#include "Timestamp.h"
#include "CurrentThread.h"
class Channel;
class Poller;
// 事件循环类 主要包含了两个大模块 Channel Poller(epoll的抽象)
class EventLoop : noncopyable
{
public:
using Functor = std::function<void()>;
EventLoop();
~EventLoop();
// 开启事件循环
void loop();
// 退出事件循环
void quit();
Timestamp pollReturnTime() const { return pollRetureTime_; }
// 在当前loop中执行
void runInLoop(Functor cb);
// 把上层注册的回调函数cb放入队列中 唤醒loop所在的线程执行cb
void queueInLoop(Functor cb);
// 通过eventfd唤醒loop所在的线程
void wakeup();
// EventLoop的方法 => Poller的方法
void updateChannel(Channel *channel);
void removeChannel(Channel *channel);
bool hasChannel(Channel *channel);
// 判断EventLoop对象是否在自己的线程里
bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); } // threadId_为EventLoop创建时的线程id CurrentThread::tid()为当前线程id
private:
void handleRead(); // 给eventfd返回的文件描述符wakeupFd_绑定的事件回调 当wakeup()时 即有事件发生时 调用handleRead()读wakeupFd_的8字节 同时唤醒阻塞的epoll_wait
void doPendingFunctors(); // 执行上层回调
using ChannelList = std::vector<Channel *>;
std::atomic_bool looping_; // 原子操作 底层通过CAS实现
std::atomic_bool quit_; // 标识退出loop循环
const pid_t threadId_; // 记录当前EventLoop是被哪个线程id创建的 即标识了当前EventLoop的所属线程id
Timestamp pollRetureTime_; // Poller返回发生事件的Channels的时间点
std::unique_ptr<Poller> poller_;
int wakeupFd_; // 作用:当mainLoop获取一个新用户的Channel 需通过轮询算法选择一个subLoop 通过该成员唤醒subLoop处理Channel
std::unique_ptr<Channel> wakeupChannel_;
ChannelList activeChannels_; // 返回Poller检测到当前有事件发生的所有Channel列表
std::atomic_bool callingPendingFunctors_; // 标识当前loop是否有需要执行的回调操作
std::vector<Functor> pendingFunctors_; // 存储loop需要执行的所有回调操作
std::mutex mutex_; // 互斥锁 用来保护上面vector容器的线程安全操作
};
EventLoop.cc
#include <sys/eventfd.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <memory>
#include "EventLoop.h"
#include "Logger.h"
#include "Channel.h"
#include "Poller.h"
// 防止一个线程创建多个EventLoop
__thread EventLoop *t_loopInThisThread = nullptr;
// 定义默认的Poller IO复用接口的超时时间
const int kPollTimeMs = 10000; // 10000毫秒 = 10秒钟
/* 创建线程之后主线程和子线程谁先运行是不确定的。
* 通过一个eventfd在线程之间传递数据的好处是多个线程无需上锁就可以实现同步。
* eventfd支持的最低内核版本为Linux 2.6.27,在2.6.26及之前的版本也可以使用eventfd,但是flags必须设置为0。
* 函数原型:
* #include <sys/eventfd.h>
* int eventfd(unsigned int initval, int flags);
* 参数说明:
* initval,初始化计数器的值。
* flags, EFD_NONBLOCK,设置socket为非阻塞。
* EFD_CLOEXEC,执行fork的时候,在父进程中的描述符会自动关闭,子进程中的描述符保留。
* 场景:
* eventfd可以用于同一个进程之中的线程之间的通信。
* eventfd还可以用于同亲缘关系的进程之间的通信。
* eventfd用于不同亲缘关系的进程之间通信的话需要把eventfd放在几个进程共享的共享内存中(没有测试过)。
*/
// 创建wakeupfd 用来notify唤醒subReactor处理新来的channel
int createEventfd()
{
int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evtfd < 0)
{
LOG_FATAL("eventfd error:%d\n", errno);
}
return evtfd;
}
EventLoop::EventLoop()
: looping_(false)
, quit_(false)
, callingPendingFunctors_(false)
, threadId_(CurrentThread::tid())
, poller_(Poller::newDefaultPoller(this))
, wakeupFd_(createEventfd())
, wakeupChannel_(new Channel(this, wakeupFd_))
{
LOG_DEBUG("EventLoop created %p in thread %d\n", this, threadId_);
if (t_loopInThisThread)
{
LOG_FATAL("Another EventLoop %p exists in this thread %d\n", t_loopInThisThread, threadId_);
}
else
{
t_loopInThisThread = this;
}
wakeupChannel_->setReadCallback(
std::bind(&EventLoop::handleRead, this)); // 设置wakeupfd的事件类型以及发生事件后的回调操作
wakeupChannel_->enableReading(); // 每一个EventLoop都将监听wakeupChannel_的EPOLL读事件了
}
EventLoop::~EventLoop()
{
wakeupChannel_->disableAll(); // 给Channel移除所有感兴趣的事件
wakeupChannel_->remove(); // 把Channel从EventLoop上删除掉
::close(wakeupFd_);
t_loopInThisThread = nullptr;
}
// 开启事件循环
void EventLoop::loop()
{
looping_ = true;
quit_ = false;
LOG_INFO("EventLoop %p start looping\n", this);
while (!quit_)
{
activeChannels_.clear();
pollRetureTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
for (Channel *channel : activeChannels_)
{
// Poller监听哪些channel发生了事件 然后上报给EventLoop 通知channel处理相应的事件
channel->handleEvent(pollRetureTime_);
}
/**
* 执行当前EventLoop事件循环需要处理的回调操作 对于线程数 >=2 的情况 IO线程 mainloop(mainReactor) 主要工作:
* accept接收连接 => 将accept返回的connfd打包为Channel => TcpServer::newConnection通过轮询将TcpConnection对象分配给subloop处理
*
* mainloop调用queueInLoop将回调加入subloop(该回调需要subloop执行 但subloop还在poller_->poll处阻塞) queueInLoop通过wakeup将subloop唤醒
**/
doPendingFunctors();
}
LOG_INFO("EventLoop %p stop looping.\n", this);
looping_ = false;
}
/**
* 退出事件循环
* 1. 如果loop在自己的线程中调用quit成功了 说明当前线程已经执行完毕了loop()函数的poller_->poll并退出
* 2. 如果不是当前EventLoop所属线程中调用quit退出EventLoop 需要唤醒EventLoop所属线程的epoll_wait
*
* 比如在一个subloop(worker)中调用mainloop(IO)的quit时 需要唤醒mainloop(IO)的poller_->poll 让其执行完loop()函数
*
* !!! 注意: 正常情况下 mainloop负责请求连接 将回调写入subloop中 通过生产者消费者模型即可实现线程安全的队列
* !!! 但是muduo通过wakeup()机制 使用eventfd创建的wakeupFd_ notify 使得mainloop和subloop之间能够进行通信
**/
void EventLoop::quit()
{
quit_ = true;
if (!isInLoopThread())
{
wakeup();
}
}
// 在当前loop中执行cb
void EventLoop::runInLoop(Functor cb)
{
if (isInLoopThread()) // 当前EventLoop中执行回调
{
cb();
}
else // 在非当前EventLoop线程中执行cb,就需要唤醒EventLoop所在线程执行cb
{
queueInLoop(cb);
}
}
// 把cb放入队列中 唤醒loop所在的线程执行cb
void EventLoop::queueInLoop(Functor cb)
{
{
std::unique_lock<std::mutex> lock(mutex_);
pendingFunctors_.emplace_back(cb);
}
/**
* || callingPendingFunctors的意思是 当前loop正在执行回调中 但是loop的pendingFunctors_中又加入了新的回调 需要通过wakeup写事件
* 唤醒相应的需要执行上面回调操作的loop的线程 让loop()下一次poller_->poll()不再阻塞(阻塞的话会延迟前一次新加入的回调的执行),然后
* 继续执行pendingFunctors_中的回调函数
**/
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup(); // 唤醒loop所在线程
}
}
void EventLoop::handleRead()
{
uint64_t one = 1;
ssize_t n = read(wakeupFd_, &one, sizeof(one));
if (n != sizeof(one))
{
LOG_ERROR("EventLoop::handleRead() reads %lu bytes instead of 8\n", n);
}
}
// 用来唤醒loop所在线程 向wakeupFd_写一个数据 wakeupChannel就发生读事件 当前loop线程就会被唤醒
void EventLoop::wakeup()
{
uint64_t one = 1;
ssize_t n = write(wakeupFd_, &one, sizeof(one));
if (n != sizeof(one))
{
LOG_ERROR("EventLoop::wakeup() writes %lu bytes instead of 8\n", n);
}
}
// EventLoop的方法 => Poller的方法
void EventLoop::updateChannel(Channel *channel)
{
poller_->updateChannel(channel);
}
void EventLoop::removeChannel(Channel *channel)
{
poller_->removeChannel(channel);
}
bool EventLoop::hasChannel(Channel *channel)
{
return poller_->hasChannel(channel);
}
void EventLoop::doPendingFunctors()
{
std::vector<Functor> functors;
callingPendingFunctors_ = true;
{
std::unique_lock<std::mutex> lock(mutex_);
functors.swap(pendingFunctors_); // 交换的方式减少了锁的临界区范围 提升效率 同时避免了死锁 如果执行functor()在临界区内 且functor()中调用queueInLoop()就会产生死锁
}
for (const Functor &functor : functors)
{
functor(); // 执行当前loop需要执行的回调操作
}
callingPendingFunctors_ = false;
}
EventLoopThread.h
#pragma once
#include <functional>
#include <mutex>
#include <condition_variable>
#include <string>
#include "noncopyable.h"
#include "Thread.h"
class EventLoop;
class EventLoopThread : noncopyable
{
public:
using ThreadInitCallback = std::function<void(EventLoop *)>;
EventLoopThread(const ThreadInitCallback &cb = ThreadInitCallback(),
const std::string &name = std::string());
~EventLoopThread();
EventLoop *startLoop();
private:
void threadFunc();
EventLoop *loop_;
bool exiting_;
Thread thread_;
std::mutex mutex_; // 互斥锁
std::condition_variable cond_; // 条件变量
ThreadInitCallback callback_;
};
EventLoopThread.cc
#include "EventLoopThread.h"
#include "EventLoop.h"
EventLoopThread::EventLoopThread(const ThreadInitCallback &cb,
const std::string &name)
: loop_(nullptr)
, exiting_(false)
, thread_(std::bind(&EventLoopThread::threadFunc, this), name)
, mutex_()
, cond_()
, callback_(cb)
{
}
EventLoopThread::~EventLoopThread()
{
exiting_ = true;
if (loop_ != nullptr)
{
loop_->quit();
thread_.join();
}
}
EventLoop *EventLoopThread::startLoop()
{
thread_.start(); // 启用底层线程Thread类对象thread_中通过start()创建的线程
EventLoop *loop = nullptr;
{
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this](){return loop_ != nullptr;});
loop = loop_;
}
return loop;
}
// 下面这个方法 是在单独的新线程里运行的
void EventLoopThread::threadFunc()
{
EventLoop loop; // 创建一个独立的EventLoop对象 和上面的线程是一一对应的 级one loop per thread
if (callback_)
{
callback_(&loop);
}
{
std::unique_lock<std::mutex> lock(mutex_);
loop_ = &loop;
cond_.notify_one();
}
loop.loop(); // 执行EventLoop的loop() 开启了底层的Poller的poll()
std::unique_lock<std::mutex> lock(mutex_);
loop_ = nullptr;
}
EventLoopThreadLoop.h
#pragma once
#include <functional>
#include <string>
#include <vector>
#include <memory>
#include "noncopyable.h"
class EventLoop;
class EventLoopThread;
class EventLoopThreadPool : noncopyable
{
public:
using ThreadInitCallback = std::function<void(EventLoop *)>;
EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg);
~EventLoopThreadPool();
void setThreadNum(int numThreads) { numThreads_ = numThreads; }
void start(const ThreadInitCallback &cb = ThreadInitCallback());
// 如果工作在多线程中,baseLoop_(mainLoop)会默认以轮询的方式分配Channel给subLoop
EventLoop *getNextLoop();
std::vector<EventLoop *> getAllLoops(); // 获取所有的EventLoop
bool started() const { return started_; } // 是否已经启动
const std::string name() const { return name_; } // 获取名字
private:
EventLoop *baseLoop_; // 用户使用muduo创建的loop 如果线程数为1 那直接使用用户创建的loop 否则创建多EventLoop
std::string name_;//线程池名称,通常由用户指定,线程池中EventLoopThread名称依赖于线程池名称。
bool started_;//是否已经启动标志
int numThreads_;//线程池中线程的数量
int next_; // 新连接到来,所选择EventLoop的索引
std::vector<std::unique_ptr<EventLoopThread>> threads_;//IO线程的列表
std::vector<EventLoop *> loops_;//线程池中EventLoop的列表,指向的是EVentLoopThread线程函数创建的EventLoop对象。
};
EventLoopThreadLoop.cc
#include <memory>
#include "EventLoopThreadPool.h"
#include "EventLoopThread.h"
#include "Logger.h"
EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg)
: baseLoop_(baseLoop), name_(nameArg), started_(false), numThreads_(0), next_(0)
{
}
EventLoopThreadPool::~EventLoopThreadPool()
{
// Don't delete loop, it's stack variable
}
void EventLoopThreadPool::start(const ThreadInitCallback &cb)
{
started_ = true;
for (int i = 0; i < numThreads_; ++i)
{
char buf[name_.size() + 32];
snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);
EventLoopThread *t = new EventLoopThread(cb, buf);
threads_.push_back(std::unique_ptr<EventLoopThread>(t));
loops_.push_back(t->startLoop()); // 底层创建线程 绑定一个新的EventLoop 并返回该loop的地址
}
if (numThreads_ == 0 && cb) // 整个服务端只有一个线程运行baseLoop
{
cb(baseLoop_);
}
}
// 如果工作在多线程中,baseLoop_(mainLoop)会默认以轮询的方式分配Channel给subLoop
EventLoop *EventLoopThreadPool::getNextLoop()
{
// 如果只设置一个线程 也就是只有一个mainReactor 无subReactor
// 那么轮询只有一个线程 getNextLoop()每次都返回当前的baseLoop_
EventLoop *loop = baseLoop_;
// 通过轮询获取下一个处理事件的loop
// 如果没设置多线程数量,则不会进去,相当于直接返回baseLoop
if(!loops_.empty())
{
loop = loops_[next_];
++next_;
// 轮询
if(next_ >= loops_.size())
{
next_ = 0;
}
}
return loop;
}
std::vector<EventLoop *> EventLoopThreadPool::getAllLoops()
{
if (loops_.empty())
{
return std::vector<EventLoop *>(1, baseLoop_);
}
else
{
return loops_;
}
}
InetAddress.h
// 封装网络地址,处理IP和端口
#pragma once
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string>
// 封装socket地址类型
class InetAddress
{
public:
explicit InetAddress(uint16_t port = 0, std::string ip = "127.0.0.1");
explicit InetAddress(const sockaddr_in &addr)
: addr_(addr)
{
}
std::string toIp() const;
std::string toIpPort() const;
uint16_t toPort() const;
const sockaddr_in *getSockAddr() const { return &addr_; }
void setSockAddr(const sockaddr_in &addr) { addr_ = addr; }
private:
sockaddr_in addr_;
};
InetAddress.cc
#include <strings.h>
#include <string.h>
#include "InetAddress.h"
InetAddress::InetAddress(uint16_t port, std::string ip)
{
::memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_port = ::htons(port); // 本地字节序转为网络字节序
addr_.sin_addr.s_addr = ::inet_addr(ip.c_str());
}
std::string InetAddress::toIp() const
{
// addr_
char buf[64] = {0};
::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);
return buf;
}
std::string InetAddress::toIpPort() const
{
// ip:port
char buf[64] = {0};
::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);
size_t end = ::strlen(buf);
uint16_t port = ::ntohs(addr_.sin_port);
sprintf(buf+end, ":%u", port);
return buf;
}
uint16_t InetAddress::toPort() const
{
return ::ntohs(addr_.sin_port);
}
#if 0
#include <iostream>
int main()
{
InetAddress addr(8080);
std::cout << addr.toIpPort() << std::endl;
}
#endif
Logger.h
#pragma once
#include <string>
#include "noncopyable.h"
// LOG_INFO("%s %d", arg1, arg2)
#define LOG_INFO(logmsgFormat, ...) \
do \
{ \
Logger &logger = Logger::instance(); \
logger.setLogLevel(INFO); \
char buf[1024] = {0}; \
snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
logger.log(buf); \
} while (0)
#define LOG_ERROR(logmsgFormat, ...) \
do \
{ \
Logger &logger = Logger::instance(); \
logger.setLogLevel(ERROR); \
char buf[1024] = {0}; \
snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
logger.log(buf); \
} while (0)
#define LOG_FATAL(logmsgFormat, ...) \
do \
{ \
Logger &logger = Logger::instance(); \
logger.setLogLevel(FATAL); \
char buf[1024] = {0}; \
snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
logger.log(buf); \
exit(-1); \
} while (0)
#ifdef MUDEBUG
#define LOG_DEBUG(logmsgFormat, ...) \
do \
{ \
Logger &logger = Logger::instance(); \
logger.setLogLevel(DEBUG); \
char buf[1024] = {0}; \
snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
logger.log(buf); \
} while (0)
#else
#define LOG_DEBUG(logmsgFormat, ...)
#endif
// 定义日志的级别 INFO ERROR FATAL DEBUG
enum LogLevel
{
INFO, // 普通信息
ERROR, // 错误信息
FATAL, // core dump信息
DEBUG, // 调试信息
};
// 输出一个日志类
class Logger : noncopyable
{
public:
// 获取日志唯一的实例对象 单例
static Logger &instance();
// 设置日志级别
void setLogLevel(int level);
// 写日志
void log(std::string msg);
private:
int logLevel_;
};
Logger.cc
#include <iostream>
#include "Logger.h"
#include "Timestamp.h"
// 获取日志唯一的实例对象 单例
Logger &Logger::instance()
{
static Logger logger;
return logger;
}
// 设置日志级别
void Logger::setLogLevel(int level)
{
logLevel_ = level;
}
// 写日志 [级别信息] time : msg
void Logger::log(std::string msg)
{
std::string pre = "";
switch (logLevel_)
{
case INFO:
pre = "[INFO]";
break;
case ERROR:
pre = "[ERROR]";
break;
case FATAL:
pre = "[FATAL]";
break;
case DEBUG:
pre = "[DEBUG]";
break;
default:
break;
}
// 打印时间和msg
std::cout << pre + Timestamp::now().toString() << " : " << msg << std::endl;
}
noncopyable.h
#pragma once // 防止头文件重复包含
/**
* noncopyable被继承后 派生类对象可正常构造和析构 但派生类对象无法进行拷贝构造和赋值构造
**/
class noncopyable
{
public:
noncopyable(const noncopyable &) = delete;
noncopyable &operator=(const noncopyable &) = delete;
// void operator=(const noncopyable &) = delete; // muduo将返回值变为void 这其实无可厚非
protected:
noncopyable() = default;
~noncopyable() = default;
};
class Singleton
{
private:
Singleton(const Singleton&)=delete;
Singleton& operator=(const Singleton&)=delete;
public:
Singleton()=default;
~Singleton()=default;
static Singleton& instance()
{
static Singleton instance;
return instance;
}
};
Poller.h
#pragma once
#include <vector>
#include <unordered_map>
#include "noncopyable.h"
#include "Timestamp.h"
class Channel;
class EventLoop;
// muduo库中多路事件分发器的核心IO复用模块
class Poller
{
public:
using ChannelList = std::vector<Channel *>;
Poller(EventLoop *loop);
virtual ~Poller() = default;
// 给所有IO复用保留统一的接口
virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0;
virtual void updateChannel(Channel *channel) = 0;
virtual void removeChannel(Channel *channel) = 0;
// 判断参数channel是否在当前的Poller当中
bool hasChannel(Channel *channel) const;
// EventLoop可以通过该接口获取默认的IO复用的具体实现
static Poller *newDefaultPoller(EventLoop *loop);
protected:
// map的key:sockfd value:sockfd所属的channel通道类型
using ChannelMap = std::unordered_map<int, Channel *>;
ChannelMap channels_;
private:
EventLoop *ownerLoop_; // 定义Poller所属的事件循环EventLoop
};
Poller.cc
#include "Poller.h"
#include "Channel.h"
Poller::Poller(EventLoop *loop)
: ownerLoop_(loop)
{
}
bool Poller::hasChannel(Channel *channel) const
{
auto it = channels_.find(channel->fd());
return it != channels_.end() && it->second == channel;
}
Socket.h
// 封装socket操作,提供绑定、监听、连接等功能
#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);
void setReuseAddr(bool on);
void setReusePort(bool on);
void setKeepAlive(bool on);
private:
const int sockfd_;
};
Socket.cc
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include "Socket.h"
#include "Logger.h"
#include "InetAddress.h"
Socket::~Socket()
{
::close(sockfd_);
}
void Socket::bindAddress(const InetAddress &localaddr)
{
if (0 != ::bind(sockfd_, (sockaddr *)localaddr.getSockAddr(), sizeof(sockaddr_in)))
{
LOG_FATAL("bind sockfd:%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 = sizeof(addr);
::memset(&addr, 0, sizeof(addr));
// fixed : int connfd = ::accept(sockfd_, (sockaddr *)&addr, &len);
int connfd = ::accept4(sockfd_, (sockaddr *)&addr, &len, SOCK_NONBLOCK | SOCK_CLOEXEC);
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)
{
// TCP_NODELAY 用于禁用 Nagle 算法。
// Nagle 算法用于减少网络上传输的小数据包数量。
// 将 TCP_NODELAY 设置为 1 可以禁用该算法,允许小数据包立即发送。
int optval = on ? 1 : 0;
::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
}
void Socket::setReuseAddr(bool on)
{
// SO_REUSEADDR 允许一个套接字强制绑定到一个已被其他套接字使用的端口。
// 这对于需要重启并绑定到相同端口的服务器应用程序非常有用。
int optval = on ? 1 : 0;
::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
}
void Socket::setReusePort(bool on)
{
// SO_REUSEPORT 允许同一主机上的多个套接字绑定到相同的端口号。
// 这对于在多个线程或进程之间负载均衡传入连接非常有用。
int optval = on ? 1 : 0;
::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
}
void Socket::setKeepAlive(bool on)
{
// SO_KEEPALIVE 启用在已连接的套接字上定期传输消息。
// 如果另一端没有响应,则认为连接已断开并关闭。
// 这对于检测网络中失效的对等方非常有用。
int optval = on ? 1 : 0;
::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}
TcpConnection.h
#pragma once
#include <memory>
#include <string>
#include <atomic>
#include "noncopyable.h"
#include "InetAddress.h"
#include "Callbacks.h"
#include "Buffer.h"
#include "Timestamp.h"
class Channel;
class EventLoop;
class Socket;
/**
* TcpServer => Acceptor => 有一个新用户连接,通过accept函数拿到connfd
* => TcpConnection设置回调 => 设置到Channel => Poller => Channel回调
**/
class TcpConnection : noncopyable, public std::enable_shared_from_this<TcpConnection>
{
public:
TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr);
~TcpConnection();
EventLoop *getLoop() const { return loop_; }
const std::string &name() const { return name_; }
const InetAddress &localAddress() const { return localAddr_; }
const InetAddress &peerAddress() const { return peerAddr_; }
bool connected() const { return state_ == kConnected; }
// 发送数据
void send(const std::string &buf);
void sendFile(int fileDescriptor, off_t offset, size_t count);
// 关闭半连接
void shutdown();
void setConnectionCallback(const ConnectionCallback &cb)
{ connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback &cb)
{ messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback &cb)
{ writeCompleteCallback_ = cb; }
void setCloseCallback(const CloseCallback &cb)
{ closeCallback_ = cb; }
void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, size_t highWaterMark)
{ highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }
// 连接建立
void connectEstablished();
// 连接销毁
void connectDestroyed();
private:
enum StateE
{
kDisconnected, // 已经断开连接
kConnecting, // 正在连接
kConnected, // 已连接
kDisconnecting // 正在断开连接
};
void setState(StateE state) { state_ = state; }
void handleRead(Timestamp receiveTime);
void handleWrite();//处理写事件
void handleClose();
void handleError();
void sendInLoop(const void *data, size_t len);
void shutdownInLoop();
void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);
EventLoop *loop_; // 这里是baseloop还是subloop由TcpServer中创建的线程数决定 若为多Reactor 该loop_指向subloop 若为单Reactor 该loop_指向baseloop
const std::string name_;
std::atomic_int state_;
bool reading_;//连接是否在监听读事件
// Socket Channel 这里和Acceptor类似 Acceptor => mainloop TcpConnection => subloop
std::unique_ptr<Socket> socket_;
std::unique_ptr<Channel> channel_;
const InetAddress localAddr_;
const InetAddress peerAddr_;
// 这些回调TcpServer也有 用户通过写入TcpServer注册 TcpServer再将注册的回调传递给TcpConnection TcpConnection再将回调注册到Channel中
ConnectionCallback connectionCallback_; // 有新连接时的回调
MessageCallback messageCallback_; // 有读写消息时的回调
WriteCompleteCallback writeCompleteCallback_; // 消息发送完成以后的回调
HighWaterMarkCallback highWaterMarkCallback_; // 高水位回调
CloseCallback closeCallback_; // 关闭连接的回调
size_t highWaterMark_; // 高水位阈值
// 数据缓冲区
Buffer inputBuffer_; // 接收数据的缓冲区
Buffer outputBuffer_; // 发送数据的缓冲区 用户send向outputBuffer_发
};
TcpConnection.cc
#include <functional>
#include <string>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/tcp.h>
#include <sys/sendfile.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include "TcpConnection.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"
static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
if (loop == nullptr)
{
LOG_FATAL("%s:%s:%d mainLoop is null!\n", __FILE__, __FUNCTION__, __LINE__);
}
return loop;
}
TcpConnection::TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr)
: loop_(CheckLoopNotNull(loop))
, name_(nameArg)
, state_(kConnecting)
, reading_(true)
, socket_(new Socket(sockfd))
, channel_(new Channel(loop, sockfd))
, localAddr_(localAddr)
, peerAddr_(peerAddr)
, highWaterMark_(64 * 1024 * 1024) // 64M
{
// 下面给channel设置相应的回调函数 poller给channel通知感兴趣的事件发生了 channel会回调相应的回调函数
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
channel_->setWriteCallback(
std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(
std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this));
LOG_INFO("TcpConnection::ctor[%s] at fd=%d\n", name_.c_str(), sockfd);
socket_->setKeepAlive(true);
}
TcpConnection::~TcpConnection()
{
LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d\n", name_.c_str(), channel_->fd(), (int)state_);
}
void TcpConnection::send(const std::string &buf)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread()) // 这种是对于单个reactor的情况 用户调用conn->send时 loop_即为当前线程
{
sendInLoop(buf.c_str(), buf.size());
}
else
{
loop_->runInLoop(
std::bind(&TcpConnection::sendInLoop, this, buf.c_str(), buf.size()));
}
}
}
/**
* 发送数据 应用写的快 而内核发送数据慢 需要把待发送数据写入缓冲区,而且设置了水位回调
**/
void TcpConnection::sendInLoop(const void *data, size_t len)
{
ssize_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
if (state_ == kDisconnected) // 之前调用过该connection的shutdown 不能再进行发送了
{
LOG_ERROR("disconnected, give up writing");
}
// 表示channel_第一次开始写数据或者缓冲区没有待发送数据
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
nwrote = ::write(channel_->fd(), data, len);
if (nwrote >= 0)
{
remaining = len - nwrote;
if (remaining == 0 && writeCompleteCallback_)
{
// 既然在这里数据全部发送完成,就不用再给channel设置epollout事件了
loop_->queueInLoop(
std::bind(writeCompleteCallback_, shared_from_this()));
}
}
else // nwrote < 0
{
nwrote = 0;
if (errno != EWOULDBLOCK) // EWOULDBLOCK表示非阻塞情况下没有数据后的正常返回 等同于EAGAIN
{
LOG_ERROR("TcpConnection::sendInLoop");
if (errno == EPIPE || errno == ECONNRESET) // SIGPIPE RESET
{
faultError = true;
}
}
}
}
/**
* 说明当前这一次write并没有把数据全部发送出去 剩余的数据需要保存到缓冲区当中
* 然后给channel注册EPOLLOUT事件,Poller发现tcp的发送缓冲区有空间后会通知
* 相应的sock->channel,调用channel对应注册的writeCallback_回调方法,
* channel的writeCallback_实际上就是TcpConnection设置的handleWrite回调,
* 把发送缓冲区outputBuffer_的内容全部发送完成
**/
if (!faultError && remaining > 0)
{
// 目前发送缓冲区剩余的待发送的数据的长度
size_t oldLen = outputBuffer_.readableBytes();
if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_)
{
loop_->queueInLoop(
std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
}
outputBuffer_.append((char *)data + nwrote, remaining);
if (!channel_->isWriting())
{
channel_->enableWriting(); // 这里一定要注册channel的写事件 否则poller不会给channel通知epollout
}
}
}
void TcpConnection::shutdown()
{
if (state_ == kConnected)
{
setState(kDisconnecting);
loop_->runInLoop(
std::bind(&TcpConnection::shutdownInLoop, this));
}
}
void TcpConnection::shutdownInLoop()
{
if (!channel_->isWriting()) // 说明当前outputBuffer_的数据全部向外发送完成
{
socket_->shutdownWrite();
}
}
// 连接建立
void TcpConnection::connectEstablished()
{
setState(kConnected);
channel_->tie(shared_from_this());
channel_->enableReading(); // 向poller注册channel的EPOLLIN读事件
// 新连接建立 执行回调
connectionCallback_(shared_from_this());
}
// 连接销毁
void TcpConnection::connectDestroyed()
{
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll(); // 把channel的所有感兴趣的事件从poller中删除掉
connectionCallback_(shared_from_this());
}
channel_->remove(); // 把channel从poller中删除掉
}
// 读是相对服务器而言的 当对端客户端有数据到达 服务器端检测到EPOLLIN 就会触发该fd上的回调 handleRead取读走对端发来的数据
void TcpConnection::handleRead(Timestamp receiveTime)
{
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0) // 有数据到达
{
// 已建立连接的用户有可读事件发生了 调用用户传入的回调操作onMessage shared_from_this就是获取了TcpConnection的智能指针
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0) // 客户端断开
{
handleClose();
}
else // 出错了
{
errno = savedErrno;
LOG_ERROR("TcpConnection::handleRead");
handleError();
}
}
void TcpConnection::handleWrite()
{
if (channel_->isWriting())
{
int savedErrno = 0;
ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);
if (n > 0)
{
outputBuffer_.retrieve(n);//从缓冲区读取reable区域的数据移动readindex下标
if (outputBuffer_.readableBytes() == 0)
{
channel_->disableWriting();
if (writeCompleteCallback_)
{
// TcpConnection对象在其所在的subloop中 向pendingFunctors_中加入回调
loop_->queueInLoop(
std::bind(writeCompleteCallback_, shared_from_this()));
}
if (state_ == kDisconnecting)
{
shutdownInLoop(); // 在当前所属的loop中把TcpConnection删除掉
}
}
}
else
{
LOG_ERROR("TcpConnection::handleWrite");
}
}
else
{
LOG_ERROR("TcpConnection fd=%d is down, no more writing", channel_->fd());
}
}
void TcpConnection::handleClose()
{
LOG_INFO("TcpConnection::handleClose fd=%d state=%d\n", channel_->fd(), (int)state_);
setState(kDisconnected);
channel_->disableAll();
TcpConnectionPtr connPtr(shared_from_this());
connectionCallback_(connPtr); // 连接回调
closeCallback_(connPtr); // 执行关闭连接的回调 执行的是TcpServer::removeConnection回调方法 // must be the last line
}
void TcpConnection::handleError()
{
int optval;
socklen_t optlen = sizeof optval;
int err = 0;
if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
{
err = errno;
}
else
{
err = optval;
}
LOG_ERROR("TcpConnection::handleError name:%s - SO_ERROR:%d\n", name_.c_str(), err);
}
// 新增的零拷贝发送函数
void TcpConnection::sendFile(int fileDescriptor, off_t offset, size_t count) {
if (connected()) {
if (loop_->isInLoopThread()) { // 判断当前线程是否是loop循环的线程
sendFileInLoop(fileDescriptor, offset, count);
}else{ // 如果不是,则唤醒运行这个TcpConnection的线程执行Loop循环
loop_->runInLoop(
std::bind(&TcpConnection::sendFileInLoop, shared_from_this(), fileDescriptor, offset, count));
}
} else {
LOG_ERROR("TcpConnection::sendFile - not connected");
}
}
// 在事件循环中执行sendfile
void TcpConnection::sendFileInLoop(int fileDescriptor, off_t offset, size_t count) {
ssize_t bytesSent = 0; // 发送了多少字节数
size_t remaining = count; // 还要多少数据要发送
bool faultError = false; // 错误的标志位
if (state_ == kDisconnecting) { // 表示此时连接已经断开就不需要发送数据了
LOG_ERROR("disconnected, give up writing");
return;
}
// 表示Channel第一次开始写数据或者outputBuffer缓冲区中没有数据
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
bytesSent = sendfile(socket_->fd(), fileDescriptor, &offset, remaining);
if (bytesSent >= 0) {
remaining -= bytesSent;
if (remaining == 0 && writeCompleteCallback_) {
// remaining为0意味着数据正好全部发送完,就不需要给其设置写事件的监听。
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
} else { // bytesSent < 0
if (errno != EWOULDBLOCK) { // 如果是非阻塞没有数据返回错误这个是正常显现等同于EAGAIN,否则就异常情况
LOG_ERROR("TcpConnection::sendFileInLoop");
}
if (errno == EPIPE || errno == ECONNRESET) {
faultError = true;
}
}
}
// 处理剩余数据
if (!faultError && remaining > 0) {
// 继续发送剩余数据
loop_->queueInLoop(
std::bind(&TcpConnection::sendFileInLoop, shared_from_this(), fileDescriptor, offset, remaining));
}
}
TcpServer.h
#pragma once
/**
* 用户使用muduo编写服务器程序
**/
#include <functional>
#include <string>
#include <memory>
#include <atomic>
#include <unordered_map>
#include "EventLoop.h"
#include "Acceptor.h"
#include "InetAddress.h"
#include "noncopyable.h"
#include "EventLoopThreadPool.h"
#include "Callbacks.h"
#include "TcpConnection.h"
#include "Buffer.h"
// 对外的服务器编程使用的类
class TcpServer
{
public:
using ThreadInitCallback = std::function<void(EventLoop *)>;
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; }
// 设置底层subloop的个数
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<std::string, TcpConnectionPtr>;
EventLoop *loop_; // baseloop 用户自定义的loop
const std::string ipPort_;
const std::string name_;
std::unique_ptr<Acceptor> acceptor_; // 运行在mainloop 任务就是监听新连接事件
std::shared_ptr<EventLoopThreadPool> threadPool_; // one loop per thread
ConnectionCallback connectionCallback_; //有新连接时的回调
MessageCallback messageCallback_; // 有读写事件发生时的回调
WriteCompleteCallback writeCompleteCallback_; // 消息发送完成后的回调
ThreadInitCallback threadInitCallback_; // loop线程初始化的回调
int numThreads_;//线程池中线程的数量。
std::atomic_int started_;
int nextConnId_;
ConnectionMap connections_; // 保存所有的连接
};
TcpServer.cc
#include <functional>
#include <string.h>
#include "TcpServer.h"
#include "Logger.h"
#include "TcpConnection.h"
static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
if (loop == nullptr)
{
LOG_FATAL("%s:%s:%d mainLoop is null!\n", __FILE__, __FUNCTION__, __LINE__);
}
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类中绑定的acceptChannel_会有读事件发生,执行handleRead()调用TcpServer::newConnection回调
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(); // 把原始的智能指针复位 让栈空间的TcpConnectionPtr conn指向该对象 当conn出了其作用域 即可释放智能指针指向的对象
// 销毁连接
conn->getLoop()->runInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
}
// 设置底层subloop的个数
void TcpServer::setThreadNum(int numThreads)
{
int numThreads_=numThreads;
threadPool_->setThreadNum(numThreads_);
}
// 开启服务器监听
void TcpServer::start()
{
if (started_.fetch_add(1) == 0) // 防止一个TcpServer对象被start多次
{
threadPool_->start(threadInitCallback_); // 启动底层的loop线程池
loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
}
}
// 有一个新用户连接,acceptor会执行这个回调操作,负责将mainLoop接收到的请求连接(acceptChannel_会有读事件发生)通过回调轮询分发给subLoop去处理
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
// 轮询算法 选择一个subLoop 来管理connfd对应的channel
EventLoop *ioLoop = threadPool_->getNextLoop();
char buf[64] = {0};
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_; // 这里没有设置为原子类是因为其只在mainloop中执行 不涉及线程安全问题
std::string connName = name_ + buf;
LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s\n",
name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());
// 通过sockfd获取其绑定的本机的ip地址和端口信息
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;
// 下面的回调都是用户设置给TcpServer => TcpConnection的,至于Channel绑定的则是TcpConnection设置的四个,handleRead,handleWrite... 这下面的回调用于handlexxx函数中
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 [%s] - connection %s\n",
name_.c_str(), conn->name().c_str());
connections_.erase(conn->name());
EventLoop *ioLoop = conn->getLoop();
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
Thread.h
#pragma once
#include <functional>
#include <thread>
#include <memory>
#include <unistd.h>
#include <string>
#include <atomic>
#include "noncopyable.h"
class Thread : noncopyable
{
public:
using ThreadFunc = std::function<void()>;
explicit Thread(ThreadFunc, const std::string &name = std::string());
~Thread();
void start();
void join();
bool started() { return started_; }
pid_t tid() const { return tid_; }
const std::string &name() const { return name_; }
static int numCreated() { return numCreated_; }
private:
void setDefaultName();
bool started_;
bool joined_;
std::shared_ptr<std::thread> thread_;
pid_t tid_; // 在线程创建时再绑定
ThreadFunc func_; // 线程回调函数
std::string name_;
static std::atomic_int numCreated_;
};
Thread.cc
#include "Thread.h"
#include "CurrentThread.h"
#include <semaphore.h>
std::atomic_int Thread::numCreated_(0);
Thread::Thread(ThreadFunc func, const std::string &name)
: started_(false)
, joined_(false)
, tid_(0)
, func_(std::move(func))
, name_(name)
{
setDefaultName();
}
Thread::~Thread()
{
if (started_ && !joined_)
{
thread_->detach(); // thread类提供了设置分离线程的方法 线程运行后自动销毁(非阻塞)
}
}
void Thread::start() // 一个Thread对象 记录的就是一个新线程的详细信息
{
started_ = true;
sem_t sem;
sem_init(&sem, false, 0); // false指的是 不设置进程间共享
// 开启线程
thread_ = std::shared_ptr<std::thread>(new std::thread([&]() {
tid_ = CurrentThread::tid(); // 获取线程的tid值
sem_post(&sem);
func_(); // 开启一个新线程 专门执行该线程函数
}));
// 这里必须等待获取上面新创建的线程的tid值
sem_wait(&sem);
}
// C++ std::thread 中join()和detach()的区别:https://blog.nowcoder.net/n/8fcd9bb6e2e94d9596cf0a45c8e5858a
void Thread::join()
{
joined_ = true;
thread_->join();
}
void Thread::setDefaultName()
{
int num = ++numCreated_;
if (name_.empty())
{
char buf[32] = {0};
snprintf(buf, sizeof buf, "Thread%d", num);
name_ = buf;
}
}
Timestamp.h
#pragma once
#include <iostream>
#include <string>
class Timestamp
{
public:
Timestamp();
explicit Timestamp(int64_t microSecondsSinceEpoch);
static Timestamp now();
std::string toString() const;
private:
int64_t microSecondsSinceEpoch_;
};
Timestamp.cc
#include <time.h>
#include "Timestamp.h"
Timestamp::Timestamp() : microSecondsSinceEpoch_(0)
{
}
Timestamp::Timestamp(int64_t microSecondsSinceEpoch)
: microSecondsSinceEpoch_(microSecondsSinceEpoch)
{
}
Timestamp Timestamp::now()
{
return Timestamp(time(NULL));
}
std::string Timestamp::toString() const
{
char buf[128] = {0};
tm *tm_time = localtime(µSecondsSinceEpoch_);
snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",
tm_time->tm_year + 1900,
tm_time->tm_mon + 1,
tm_time->tm_mday,
tm_time->tm_hour,
tm_time->tm_min,
tm_time->tm_sec);
return buf;
}
// #include <iostream>
// int main() {
// std::cout << Timestamp::now().toString() << std::endl;
// return 0;
// }
CMakeLists.txt
#设置最低版本和项目名称
cmake_minimum_required(VERSION 3.0)
project(muduo-core) #锁定项目的工作目录
#设置全局的c++标准
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED True)
#链接必要的库,比如刚刚我们写好的在src文件Cmakelists中muduo-core_lib静态库,还有全局链接库
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
#示例静态库的输出
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) # 设置静态库输出路径
#设置全局链接库
set(LIBS
pthread
)
#添加子目录
add_subdirectory(src)
add_subdirectory(example)
运行代码 cmake
mkdir build && cd build && cmake .. && make -j$(nproc)
执行 ./testserver
显示日志
从日志来看,testserver 已经成功运行,并且服务器在 127.0.0.1:8080 上监听连接。以下是日志中的关键点:
服务器启动:
[INFO]2025/04/13 11:22:59 : EventLoop ... start looping 表明事件循环已经启动。
[INFO]2025/04/13 11:22:59 : func=poll => fd total count:1 表示服务器正在监听文件描述符。
新连接建立:
[INFO]2025/04/13 11:23:03 : TcpServer::newConnection [EchoServer] - new connection [EchoServer-127.0.0.1:8080#1] from 127.0.0.1:51641 表示有客户端连接到服务器。
[INFO]2025/04/13 11:23:03 : Connection UP : 127.0.0.1:51641 表明连接已成功建立。
连接关闭:
[INFO]2025/04/13 11:23:03 : Connection DOWN : 127.0.0.1:51641 表示客户端断开连接。
多次连接:
日志显示多个客户端连接和断开,服务器处理正常。
还可以:
telnet 127.0.0.1 8080
echo "Hello, World!" | nc 127.0.0.1 8080
服务器应该返回 Hello, World!