版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84402031
TCP网络编程本质
TCP网络编程最本质的是处理三个半事件
- 连接建立:服务器accept(被动)接收连接,客户端connect(主动)发起连接
- 连接断开:主动断开(close、shutdown),被动断开(read返回0)
- 消息到达:文件描述符可读
- 消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;这里的发送完毕是指数据写入操作系统缓冲区,将由TCP协议栈负责数据的发送与重传,不代表对方已经接收到数据
EventLoop是整个Reactor的核心。其类图如下:
one loop per thread意味着每个线程只能有一个EventLoop对象,用变量
__thread EventLoop* t_loopInThisThread = 0;
表示,在创建EventLoop对象时将t_loopInThisThread赋值,以后再创建时就可以检查这个变量,如果已经赋值就说明当前线程已经创建过EventLoop对象了。线程调用静态函数EventLoop::getEventLoopOfCurrentThread就可以获得当前线程的EventLoop对象的指针了。
对EventLoop的个人理解
- 与libevent的event_base类似,EventLoop也是一个事件循环,即EventLoop本质上是一个while循环,在while循环中调用了epoll_wait/poll监听注册在EventLoop上的事件/Channel
- EventLoop的使用与libevent的event_base一样,就是调用loop()函数对安插在EventLoop对象上的事件进行监听
EventLoop::loop()的时序图
- loop.loop()实际上是在while循环中调用了poll或epoll的poll函数,程序在此处阻塞,直到有事件被触发
- 当监听到事件被触发后,调用handleEvent()函数去处理事件
//事件循环,该函数不能跨线程调用,只能在创建该对象的线程中调用
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread(); //断言当前处于创建该对象的线程中
looping_ = true;
quit_ = false; // FIXME: what if someone calls quit() before loop() ?
LOG_TRACE << "EventLoop " << this << " start looping";
while (!quit_)
{
//清除活动通道
activeChannels_.clear();
//调用poll()[ 本质上epoll的epoll_wait、poll的poll() ],阻塞在这里
//poll返回后,activeChannels_活动通道被填充
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
++iteration_;
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
eventHandling_ = true;
//遍历每一个活动通道,并处理事件handleEvent
for (Channel* channel : activeChannels_)
{
currentActiveChannel_ = channel;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
doPendingFunctors(); //执行pendingFunctors_中的任务
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
示例代码
父子线程都创建了自己的loop,并且监控安插在各自身上的事件
#include <muduo/net/EventLoop.h>
#include <muduo/base/Thread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
void callback()
{
printf("callback(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
EventLoop anotherLoop;
}
void threadFunc()
{
printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
assert(EventLoop::getEventLoopOfCurrentThread() == NULL);
EventLoop loop; //子线程中的loop
assert(EventLoop::getEventLoopOfCurrentThread() == &loop);
loop.runAfter(1.0, callback); //1s后,调用callback
loop.loop();
}
int main()
{
printf("main(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
assert(EventLoop::getEventLoopOfCurrentThread() == NULL);
EventLoop loop; //主线程中的loop
assert(EventLoop::getEventLoopOfCurrentThread() == &loop);
Thread thread(threadFunc);
thread.start(); //启动子线程
loop.loop(); //主线程中的loop调用loop()
}