ZLToolKit开源网络库源码剖析
说真的,我真受不了稀土掘金这么久了还没有支持字体颜色,居然连下划线的功能都没有,后续不在这个上面更新文章,太难用了。 后续考虑去CSDN去更新文章了,大家可以去我的主页看最新文章。名字和头像和当前这个一样
大家觉得掘金排版不好看,可以去我的语雀去看。 语雀链接,默认半年到期看不了评论联系我
EventPoller [事件循环] -> src/NetWork/EventPoller.h
这个项目最主要的就是这个,搞懂这个原理,其他都好说了。后续画个关系图
EventPollerPool 事件循环池
描述
Why? 创建多个线程(默认是CPU数量),每个线程又创建出EventPoller,每个EventPoller底层又是用epoll处理事件的。猜测作者的意思将一个socket的事件注册多个epoll上,然后每个epoll又是不同的线程再监听,有任何一个线程监听就可以进行处理了,提高处理效率。
但是,数据包到达本机,底层是内核线程来处理调用协议栈,根据目标端口和目标ip地址找到对应的socket结构体,根据已打开的文件描述符列表找到对应的epoll红黑树节点,唤醒之前调用epoll_wait等待的线程。所以这一套流程,创建多个线程监听完全没必要,还浪费内存,还会造成一些竞争问题。
这是自己的见解,我觉得这是个问题
源码剖析
1. 默认构造
cpp
static size_t s_pool_size = 0;
static bool s_enable_cpu_affinity = true;
void EventPoller::runLoop(bool blocked, bool ref_self)
{
if (blocked)
// 代码省略...
else
{
// 执行到这里,一般是主线程创建EventPoller
// 下面的addPoller会执行到这里
_loop_thread = new thread(&EventPoller::runLoop, this, true, ref_self);
_sem_run_started.wait(); // 等待被创建处理来的线程唤醒
}
}
size_t TaskExecutorGetterImpl::addPoller(const string& name, size_t size, int priority,
bool register_thread, bool enable_cpu_affinity)
{
// 获取CPU核心数
auto cpus = thread::hardware_concurrency();
size = size > 0 ? size : cpus; // 如果要创建的EventPoller数量大于0,则设置为参数size,否则就是cpu线程个数
// 创建具体数量的线程
for (size_t i = 0; i < size; i++)
{
auto full_name = name + " " + to_string(i); // 设置线程的名称
auto cpu_index = i % cpus;
// 创建EventPoller对象
EventPoller::Ptr poller(new EventPoller(full_name));
// 调用runLoop,这里会创建线程,看上面runLoop函数代码,参数传递的是 { false, register_thread }
poller->runLoop(false, register_thread);
// 异步由EventPoller执行该任务
poller->async([cpu_index, full_name, priority, enable_cpu_affinity]() {
// 设置该线程优先级
ThreadPool::setPriority((ThreadPool::Priority)priority);
setThreadName(full_name.data()); // 设置线程名称
if (enable_cpu_affinity)
setThreadAffinity(cpu_index); // 设置线程亲和性
});
// 将该线程保存起来
_threads.emplace_back(std::move(poller));
}
}
// 构造函数
EventPollerPool::EventPollerPool()
{
// 创建多个的EventPoller
auto size = addPoller("event poller", s_pool_size, ThreadPool::PRIORITY_HIGHEST,
true, s_enable_cpu_affinity);
}
2. 从池中获取一个EventPoller对象
cpp
static thread_local std::weak_ptr<EventPoller> s_current_poller; // thread_local修饰的变量为每个线程独有的
// static
EventPoller::Ptr EventPoller::getCurrentPoller()
{
return s_current_poller.lock();
}
// prefer_current_thread: 是否优选选择当前线程
EventPoller::Ptr EventPollerPool::getPoller(bool prefer_current_thread)
{
auto poller = EventPoller::getCurrentPoller(); // 获取属于当前线程的EventPoller对象
// 判断是否优先选择当前线程,如果是则返回
if (prefer_current_thread && _prefer_current_thread && poller)
return poller;
// 如果不是,则选择一个负载最小的线程
return static_pointer_cast<EventPoller>(getExecutor());
}
cpp
EventPoller::Ptr EventPollerPool::getFirstPoller()
{
// 从创建的线程里面选择第一个并返回
return static_pointer_cast<EventPoller>(_threads.front());
}
事件循环
描述
Why? 底层epoll 监听事件。每个EventPoller有自己的线程。核心函数是 runLoop() ,该函数调用epoll_wait然后执行该事件所绑定的回调函数。
作者使用EventPoller的想法是: 创建出多个线程,每个线程又是独立的EventPoller。每个线程去监听事件,调用该事件所对应的回调函数。
源码剖析
1. 默认构造
cpp
EventPoller::EventPoller(std::string name)
{
_event_fd = create_event(); // 创建一个epoll
if (_event_fd == -1)
throw runtime_error(StrPrinter << "Create event fd failed: " << get_uv_errmsg());
SockUtil::setCloExec(_event_fd);
_name = std::move(name);
_logger = Logger::Instance().shared_from_this();
addEventPipe(); // epoll去监听当前管道的读、写事件
}
上面代码所做的事情:
创建epoll
调用addEventPipe(),用epoll去监听管道的读、写事件。主要目的是为了事件通知,比如有其他线程提交了任务,则会唤醒主线程去执行这个任务
2. addEventPipe() 监听管道的读、写事件
cpp
void EventPoller::addEventPipe()
{
SockUtil::setNoBlocked(_pipe.readFD());
SockUtil::setNoBlocked(_pipe.writeFD());
// 监听管道的读事件
if (addEvent(_pipe.readFD(), EventPoller::Event_Read, [this](int event) { onPipeEvent(); }) == -1 )
throw std::runtime_error("Add pipe fd to poller failed");
}
3. addEvent添加监听事件
cpp
void EventPoller::addEventPipe()
{
SockUtil::setNoBlocked(_pipe.readFD());
SockUtil::setNoBlocked(_pipe.writeFD());
// 监听管道的读事件
if (addEvent(_pipe.readFD(), EventPoller::Event_Read, [this](int event) { onPipeEvent(); }) == -1 )
throw std::runtime_error("Add pipe fd to poller failed");
}
4. delEvent 删除监听事件
cpp
int EventPoller::delEvent(int fd, PollCompleteCB cb)
{
TimeTicker();
if (!cb)
cb = [](bool success) {};
// 如果是当前线程
if (isCurrentThread())
{
int ret = -1;
// 首先从map中删除
if (_event_map.erase(fd))
{
// 保存到缓存里
_event_cache_expired.emplace(fd);
// 从epoll中删除
ret = epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr);
}
cb(ret != -1); // 调用回调函数
return ret;
}
// 如果是非当前线程,则异步交给删除事件
async([this, fd, cb]() mutable {
delEvent(fd, std::move(cb));
});
return 0;
}
5. modifyEvent 修改事件
cpp
int EventPoller::modify(int fd, int event, PollCompleteCB cb)
{
TimeTicker();
if (!cb)
cb = [](bool success) {};
// 如果是当前线程
if (isCurrentThread())
{
struct epoll_event ev = {0};
ev.events = toEpoll(event);
ev.data.fd = fd;
// 修改epoll的事件
auto ret = epoll_ctl(_event_fd, EPOLL_CTL_MOD, fd, &ev);
cb(ret != -1); // 调用回调函数
return ret;
}
// 如果是非当前线程,则异步添加,然后由主线程去执行
async([this, fd, event, cb]() mutable {
modifyEvent(fd, event, std::move(cb));
});
return 0;
}
6. async异步提交任务
cpp
Task::Ptr EventPoller::async(TaskIn task, bool may_sync)
{
return async_l(std::move(task), may_sync, false);
}
Task::Ptr EventPoller::async_first(TaskIn task, bool may_sync)
{
return async_l(std::move(task), may_sync, true);
}
Task::Ptr EventPoller::async_l(TaskIn task, bool may_sync, bool first)
{
TimeTicker();
// 如果需要同步并且也是当前线程
if (may_sync && isCurrentThread())
{
task(); // 则直接执行任务
return nullptr;
}
auto ret = std::make_shared<Task>(std::move(task));
{
lock_guard<mutex> lock(_mtx_task);
if (first) // 如果选择插入到队列头
_list_task.emplce_front(ret);
else
_list_task.emplace_back(ret);
}
// 生成一个事件,唤醒主线程来执行这个任务
_pipe.wrte("", 1);
return ret;
}
7. isCurrentThread 判断是否是当前线程
cpp
class EventPoller
{
private:
// 执行事件循环的线程
std::thread* _loop_thread = nullptr;
};
bool EventPoller::isCurrentThread()
{
// 每个EventPoller有独立的线程
return !_loop_thread || _loop_thread->get_id()
== this_thread::get_id();
}
8. doDelayTask 延时执行某个任务
cpp
/*
delay_ms: 延时的时间
task: 要延时执行的任务
*/
EventPoller::DelayTask::Ptr
EventPoller::doDelayTask(uint64_t delay_ms, function<uint64_t()> task)
{
DelayTask::Ptr ret = std::make_shared<DelayTask>(std::move(task));
// 设置延时执行任务的那个时间
auto time_line = getCurrentMillisecond() + delay_ms;
// 将该任务以优先异步的执行方式提交
async_first([time_line, ret, this]() {
_delay_task_map.emplace(time_line, ret);
});
return ret;
}
9. getSharedBuffer 获取当前线程下所有的socket共享的读缓存
cpp
class EventPoller
{
private:
// _shared_buffer[0] 存储的是tcp数据
// _shared_buffer[1] 存储的是udp数据
std::weak_ptr<SocketRecvBuffer> _shared_buffer[2];
};
SocketRecvBuffer::Ptr getSharedBuffer(bool is_udp)
{
// 获取对应的缓存对象
auto ret = _shared_buffer[is_udp].lock();
if (!ret) // 如果还未创建
{
// 创建这个对象
ret = SocketRecvBuffer::create(is_udp);
// 存储起来
_shared_buffer[is_udp] = ret;
}
return ret;
}
10. runLoop 执行事件循环的函数,最主要的
cpp
// 每个线程有属于自己的这个变量
static thread_local std::weak_ptr<EventPoller> s_current_poller;
/*
blocked: 是否设置为调用该函数的线程执行轮询,如果是,存储到s_current_poller
ref_self: 是记录本对象到thread local变量
*/
void EventPoller::runLoop(bool blocked, bool ref_self)
{
if (blocked)
{
if (ref_self)
s_current_poller = shared_from_this();
_sem_run_started.post(); // 唤醒创建当前线程的线程
_exit_flag = false;
uint64_t minDelay;
struct epoll_event events[EPOLL_SIZE];
// 执行轮询循环
while (!_exit_flag)
{
minDelay = getMinDelay();
startSleep(); // 统计当前线程负载情况
int ret = epoll_wait(_event_fd, events, EPOLL_SIZE, minDelay ? minDelay : -1);
sleepWakeUp(); // 统计当前线程负载情况,也就当前线程等待事件发生所用的时间
if (ret <= 0)
continue; // 没有事件发生,或有错误,重新来
// 这个是存储之前被删除过的事件
_event_cache_expired.clear();
// 循环处理所发生的事件
for (int i = 0; i < ret; i++)
{
struct epoll_event& ev = events[i];
int fd = ev.data.fd;
// 这块有问题,上面clear()后,调用count()只会返回0,没什么意义
if (_event_cache_expired.count(fd))
continue;
// 根据fd从存储所有事件集合中去查找
auto it = _event_map.find(fd);
if (it == _event_map.end()) // 如果没有找到,则说明这个fd以前被删除过
{
// 所以也从epoll中将其删除
epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr);
continue;
}
// 查找到,则获取该fd所绑定的回调
auto cb = it->second;
try {
// 执行该fd所对应的回调函数
(*cb)(toPoller(ev.events));
} catch (std::exception& ex)
{
// 日志输出
}
}
}
}
else
{
// 执行到这里,一般是主线程创建EventPoller
// 创建线程,线程执行函数是runLoop
_loop_thread = new thread(&EventPoller::runLoop, this, true, ref_self);
_sem_run_started.wait(); // 等待被创建处理来的线程唤醒
}
}
11. onPipeEvent 当收到管道的读事件所执行的函数
cpp
inline void EventPoller::onPipeEvent(bool flush)
{
char buf[1024];
int err = 0;
if (!flush)
{
while (true)
{
// 从管道一直读取数据,直到读不出来
// 这里将管道设置的是非阻塞,所以如果没有数据则会返回
if ((err = _pipe.read(buf, sizeof(buf))) > 0)
continue;
// 非阻塞的管道如果返回0则代表被关闭了
// 非阻塞的管道只会返回大于0或者-1然后设置errno为具体的错误,比如EOF、EAGAIN
if (err == 0 || get_uv_error(true) != UV_EAGIN)
{
// 从epoll删除该管道的读事件
delEvent(_pipe.readFD());
_pipe.reOpen(); // 将管道重新打开
addEventPipe(); // 然后重新注册管道的事件
}
break;
}
}
// 这里将异步执行的任务全部swap出来
decltype(_list_task) _list_swap;
{
lock_guard<mutex> lock(_mtx_task);
_list_swap.swap(_list_task);
}
// 循环遍历,并一个一个处理需要异步执行的任务
_list_swap.for_each([&](const Task::Ptr& task) {
try {
(*task)(); // 调用回调函数
} catch(ExitException& ) {
_exit_flag = true;
} catch (std::exception& ex) {
// 日志
}
});
}
剩下一些函数可以自己去看懂了,下面画一个流程图,清晰的解释一下这个类的工作原理
TcpServer [提供Tcp服务] -> src/Network/TcpServer.h
分析这个之前,我们先分析跟TcpServer相关的两个类 Session(跟连接会话相关)和Socket(跟套接字操作相关,比如读数据)
Session [管理连接的会话] -> src/Network/Session.h
描述
why? 每次连接成功都会生成一个"会话",提供一些功能,比如说向对端发送数据、接收到数据该如何去做、以及发生错误时候该怎么。那么就衍生出这个Session类
通过上面这行描述,它肯定是跟Socket必不可分,所以下面这关系图也表明了。
我们想要处理和对端的接收、发送数据逻辑,那么我们就需要继承自Session类,然后实现它提供的几个 纯虚函数: onRecv() 、onError()、onFlush()、onManager()
下面是Session类的继承体系,建议先过一遍,脑子里有个印象。可以通过类名大概推断出这几个类他们的含义是什么,下面逐个讲解:
SocketHelper [ 封装Socket类和EventPoller绑定 ] -> src/NetWork/Socket.h
该类最主要的是提供几个纯虚函数接口,用于接收发送消息的处理。用户只需要实现这些函数,由内部最终调用这些函数,而不需要关心该什么时候发送、什么时候接收,只需要专注于自己的处理逻辑即可。 还有和EventPoller(单独的线程)进行绑定,可以提交任务交给 单独的线程EventPoller去执行。
SocketSender、SockInfo [用于发送数据、获取关于网络信息] -> src/NetWork/Socket.h
SocketSender重载了一些 "输入运算符"以及提供了send发送数据接口。 SockInfo提供了一些获取网络信息的接口,比如获取本地ip、本地端口、目标ip、目标端口。
终于到了Session类,看上图,它提供两个函数 attachServer : TcpServer创建Session后会把它自身的信息传递进来,如果用户想做一些处理,可以重写这个函数,加上自己的处理逻辑 getIdentifier: 每个Session都有唯一的标识用来区分,所以这个函数就是用来获取标识
Session类讲解到这里,让我们分析TcpServer类
TcpServer类的UML图如下:
下面详细描述一下这个类的工作原理,看不懂描述后面看执行流程图,然后回来再看描述就懂了
这里只专注讲TcpServer的工作原理,想彻底搞明白它到底是怎么做的,肯定会牵扯更多其他的类,所以这个时候不要心急一次性搞懂,先慢慢差不多理解一个类的原理,再去搞懂其他的类原理,就会把关系网连上。
原理:
看上面的UML图,得知这个TcpServer也是深深和EventPoller绑定的,通过前面分析,我们知道一个EventPoller就是一个线程。
所以监听客户端的连接和接收、发送数据也都是EventPoller去做的,然后我们提供onRecv以及onError等回调函数,内部会调用。这样就将这些关于网络处理的细节对用于隐藏了起来
该类的构造函数判断你是从EventPollerPool中自动选择一个EventPoller,还是你主动指定绑定到某个EventPoller。
当我们调用start函数,设置创建session的函数。然后调用启动服务的核心函数 start_l()。在该函数,调用 setupEvent() ,该函数创建一个Socket类(封装了很多网络处理的函数,比如listen、bind这些),但这时并没有真正创建套接字。还设置了当接收客户端连接时应该做哪些事情,以及接收客户端连接之前做什么事情。这个很重要,有客户端数据到来,内部才会调用我们继承Session的onRecv() 函数。
为当前TcpServer创建一个套接字,并绑定地址信息、调用listen(),进行监听客户端连接。
如果你从EventPollerPool中获取EventPoller的话,会为每个EventPoller创建一个Server,配置则从当前TcpServer复制,复制出的Server不会创建套接字,而是和当前TcpServer共享一个套接字。复制出来的这些Server就存储成员变量: _cloned_server中。这些复制的Server有个 _parent 成员变量指向当前TcpServer,表明当前才是主Server。
然后将其他的EventPoller也监听当前TcpServer所创建的套接字。至此,启动服务结束
上面简要介绍了TcpServer类的大概原理,也重点指出了有客户端连接该怎么做,以及发送过来的数据怎么调用我们的函数的(关于如何调用我们的函数,这就是其他类的处理,后面分析)。下面开始源码剖析,慢慢看完代码再加上最后的执行流程图就完全明白了,脑子里先有个大概理解。
源码剖析:
- 构造函数
cpp
// 构造函数接收一个EventPoller参数,可以指定到某个EventPoller
TcpServer::TcpServer(const EventPoller::Ptr &poller) : Server(poller)
{
// 如果没指定poller,那么将_multi_poller标志位设置为true,表示从EventPoller池中获取一个
_multi_poller = !poller;
setOnCreateSocket(nullptr); // 设置创建socket的回调函数,没指定则调用Socket::createSocket函数
}
- start启动服务
cpp
/*
port: 要监听的端口号
host: 绑定的ip地址
backlog: 监听队列大小
callback: 当接收到客户端连接时,会创建一个Session,那么创建Session的回调函数就会调用这个回调函数
下面模板类型SeesionType其实就是我们继承Seesion这个类的派生类类型
*/
template <typename SeesionType>
void start(uint16_t port, const std::string& host = "::", uint32_t backlog = 1024,
const std::function<void(std::shared_ptr<SessionType>&)>& callback = nullptr)
{
// 获取继承自Seesion的类的类型
// 比如 class MySession : public seesion
// 那么cls_name就是 MySession
static std::string cls_name = toolkit::demangle(typeid(SessionType).name());
// 设置创建Session的回调函数
_seesion_alloc = [cb](const TcpServer::Ptr& server, const Socket::Ptr& sock) {
// 创建我们传入的继承Seesion的派生类
auto session = std::shared_ptr<SessionType>(new SessionType(sock), [](SessionType* ptr) {
delete ptr;
});
// 如果设置了回调,则在这里执行
if (cb) {
cb(seesion);
}
// 创建一个Socket类
session->setOnCreateSocket(sever->_on_create_socket);
// 将Seesion、TcpServer、类名分装成SeesionHelper返回
return std::make_shared<SessionHelper>(server, std::move(session), cls_name);
};
// 真正启动服务的代码在这个函数里
start_l(port, host, backlog);
}
- start_l 真正启动服务的函数
cpp
void TcpServer::start_l(uint16_t port, const std::string& host, uint32_t backlog)
{
// 创建socket、设置客户端建立连接执行的回调函数
setupEvent();
// 获取当前TcpServer的引用
weak_ptr<TcpServer> weak_self = std::static_pointer_cast<TcpServer>(shared_from_this());
// 创建一个定时器,定时的执行onManagerSeesion,管理seesion
_timer = std::make_shared<Timer>(2.0f, [weak_self]() -> bool {
auto strong_self = weak_self.lock();
if (!strong_self) {
return false;
}
strong_self->onManagerSession();
return true;
}, _poller);
// 如果是从EventPoller池中获取的EventPoller
if (_multi_poller)
{
// 遍历EventPollerPool的每一个Poller
EventPollerPool::Instance().for_each([&](const EventPoller::Ptr poller =
static_pointer_cast<EventPoller>(executor);
if (poller == _poller)
return;
// 设置一个拷贝Server,_cloned_server是unordered_map类型
auto& serverRef = _cloned_server[poller.get()];
if (!serverRef) {
// 为这个Poller创建一个Server
serverRef = onCreateServer(poller);
}
if (serverRef) {
// 拷贝当前主TcpServer的一些配置信息
serverRef->cloneFrom(*this);
}
});
}
// 为当前Server创建套接字,并进行监听
if (!_socket->listen(port, host.c_str(), backlog))
throw std::runtime_error(err);
// 如果是从EventPoller池中获取Poller的,那么就有复制的Server
// 这里将其他的EventPoller也监听当前主TcpServer套接字
for (auto& pr = _cloned_server)
pr.second->_socket->cloneSocket(*_socket);
}
- setupEvent 设置连接时执行的回调
cpp
void TcpServer::setupEvent()
{
// 创建socket,设置客户端连接时的回调函数
_socket = createSocket(_poller);
// 获取当前类的引用
weak_ptr<TcpServer> weak_self = std::static_pointer_cast<TcpServer>(shared_from_this());
// 设置accept连接 之前 调用的回调
_socket->setOnBeforeAccept([weak_self](const EventPoller::Ptr& poller) -> Socket::Ptr {
// 获取当前对象的强引用
if (auto strong_self : weak_self.lock()) {
// 为此连接创建一个socket对象,这是下面这个函数所作的事情
return strong_self->onBeforeAcceptConnection(poller);
}
return nullptr;
});
// 设置accept连接的回调函数
_socket->setOnAccept([weak_self](Socket::Ptr& sock, shared_ptr<void>& complete) {
// 获取当前对象的强引用
if (auto strong_self = weak_self.lock()) {
// 获取当前socket所使用的poller
auto ptr = sock->getPoller().get();
// 根据poller获取对应的server
auto server = strong_self->getSever(ptr);
// 异步提交一个任务给poller
ptr->async([server, sock, complete]() {
// 这个函数设置接收数据的回调、发生错误的回调
server->onAcceptConnection(sock);
});
}
});
}
- onAcceptConnection 客户端连接时所执行的函数,用来设置一个回调
cpp
Seesion::Ptr TcpServer::onAcceptConnection(const Socket::Ptr& sock)
{
// 获取当前对象的引用
weak_ptr<TcpServer> weak_self = std::static_pointer_cast<TcpServer>(shared_from_this());
// 创建一个SeesionHelper对象,返回的是一个智能指针
auto helper = _session_alloc(std::static_pointer_cast<TcpServer>(shared_from_this()), this);
auto session = helper->seesion(); // 通过SeesionHelper获取Seesion对象
seesion->attachServer(*this); // 将当前Server的配置传递给seesion
// 将封装Seesion的SeesionHelper保存起来,返回一个值表示是否插入成功
auto success = _seesion_map.emplace(helper.get(), helper).second;
weak_ptr<Session> weak_seesion = seesion;
// 设置读取数据的回调
sock->setOnRead([weak_seesion](const Buffer::Ptr& buf, struct sockaddr*, int) {
auto strong_seesion = wea_seesion.lock(); // 获取seesion的强引用
if (!strong_seesion)
return;
try {
strong_seesion->onRead(buf); // 调用seesion的回调函数 onRead
} catch (SockException& ex) {
strong_seesion->shutdown(ex);
} catch (exception& ex) {
strong_seesion->shutdown(SockException(Err_shutdown, ex.what()));
}
});
// helper是一个智能指针,调用get() 返回指针
SeesionHelper* ptr = helper.get();
auto cls = ptr->className(); // 获取类名
// 设置错误的回调函数
sock->setOnErr([weak_self, weak_seesion, ptr, cls](const SockException& err) {
// onceToken是RAII,两个参数是构造和析构执行的
// 下面没有设置构造时执行的函数,只设置了析构时执行的函数
onceToken token(nullptr, [&]() {
auto strong_self = weak_self.lock();
if (!strong_self)
return;
// 如果当前没有在执行管理seesion
if (!strong_self->_is_on_mananger) {
// 这里从session map中删除
strong_self->_session_map.erase(ptr);
}
else
{
// 异步提交任务
strong_self->_poller->async([weak_self, ptr]() {
auto strong_self = weak_self.lock();
if (strong_self)
strong_self->_seesion_map.erase(ptr);
}, false);
}
});
// 获取Seesion的强引用
auto strong_session = weak_seesion.lock();
if (strong_seesion)
strong_seesion->onError(err); // 调用seesion的错误处理函数onError
});
return seesion;
}
- onManagerSeesion 管理Seesion的回调函数
cpp
void TcpServer::onManagerSession()
{
// RAII方式,这里设置了构造调用的函数
onceToken token([&]() {
_is_on_manager = true;
}, [&]() {
_is_on_manager = false;
});
// _seesion_map保存了创建出来的seesion
for (auto& pr : _seesion_map)
{
try {
// 调用seesion的管理函数
pr.second->seesion->onManager();
} catch (exception& ex) {
WarnL << ex.what();
}
}
}
- getServer 根据poller获取TcpServer
cpp
TcpServer::Ptr TcpServer::getServer(const EventPoller* poller) const
{
// 获取parent Server,这个是自己作为复制出来的Server,才会有parent(也就是主TcpServer)
auto parent = _parent.lock();
// 如果没有父 server,那么当前就是主Server。否则从父Server的_cloned_server(复制出来的Server)中获取
auto &ref = parent ? parent->_cloned_server : _cloned_server;
auto it = ref.find(poller); // 然后根据poller查找对应的Server
if (it != ref.end())
return it->second; // 找到了则返回
// 没有找到的话,则返回主TcpServer
return static_pointer_cast<TcpServer>(parent ? parent : const_cast<TcpServer*>(this)->shared_from_this());
}
- cloneFrom 从主Server复制出Server
cpp
// 从主Server复制出来的Server,则调用这个函数完成
void TcpServer::cloneFrom(const TcpServer& that)
{
if (!that._socket)
throw std::invalid_argument("xxxx");
setupEvent(); // 创建Socket对象(不是套接字,内部封装的)。然后设置一些回调
_main_server = false; // 不是主服务,因为当前是被复制出来的
_on_create_socket = that._on_create_socket; // 创建socket的回调也是复制主服务的
_seesion_alloc = that._seesion_alloc; // 同样分配seesion的函数也是从主服务复制出来的
// 获取当前对象的引用
weak_ptr<TcpServer> weak_self = std::static_pointer_cast<TcpServer>(shared_from_this());
// 这里创建个定时器,定时执行onManagerSeesion进行管理
_timer = std::make_shared<Timer>(2.0f, [weak_self]() -> bool {
auto strong_self = weak_self.lock();
if (!strong_self)
return false;
strong_self->onManagerSeesion();
return return;
}, _poller);
this->mINI::operator=(that);
// 指向主服务
_parent = static_pointer_cast<TcpServer>(const_cast<TcpServer&>(that).shared_from_this());
}
程序分析图:
- TcpServer的结构图
注意: 这是未指定EventPoller的结构图
- TcpServer的执行流程图
上面这个执行流程图只是指明了主TcpServer针对事件发生时,所执行的过程。注意还有复制的TcpServer当他们监听有事件发生后,也和TcpServer执行同样的过程。
上面对于未指定EventPoller做了详细的说明,指定了EventPoller比上面简单很多,这个可以自行分析了。难的都会了,那简单的更不用说了。