muduo网络库学习之EventLoop(一):事件循环类图简介

番外

TCP网络编程本质 

 

TCP网络编程最本质是的处理三个半事件

连接建立:服务器accept(被动)接受连接,客户端connect(主动)发起连接

连接断开:主动断开(close、shutdown),被动断开(read返回0)

消息到达:文件描述符可读
 

消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;这里的发送完毕是指数据写入操作系统缓冲区,将由TCP协议栈负责数据的发送与重传,不代表对方已经接收到数据。

muduo网络库学习之EventLoop(一):事件循环类图简介_第1张图片

当一个套接字有数据到来的时候,先被内核接受到内核缓冲区,然后网络库的可读事件触发,将数据从内核移动到应用层的缓冲区中。并且网络库回调一个函数,比如OnMessage,这个函数会根据协议判断数据包是否是一个完整的数据包,如果不是,OnMessage立刻返回,直到内核中又接受了一些数据,这时候网络库时间循环中的可读事件又触发了,又把数据从内核缓冲区移到了应用缓冲区中,又回调OnMessage函数,如果是完整数据包,就把它读走、解包、计算处理、打包,发送。

write(buf,...)的时候,如果数据全部填到了内核缓冲区,网络库回调OnWriteComplete,如果内核缓冲区不足以容纳数据,要把数据追加到应用层的发送缓冲区。如果内核发送缓冲区有空间了,就会触发套接口的可写事件,就会将应用层发送缓冲区的数据添加到内核发送缓冲区。

什么都不做的EventLoop

one loop per thread意思是说每个线程最多只能有一个EventLoop对象。

EventLoop对象构造的时候,会检查当前线程是否已经创建了其他EventLoop对象,如果已创建,终止程序(LOG_FATAL)

EventLoop构造函数会记住本对象所属线程(threadId_)。
 

创建了EventLoop对象的线程称为IO线程,其功能是运行事件循环(EventLoop::loop)

 

类图

muduo网络库学习之EventLoop(一):事件循环类图简介_第2张图片

 

白色部分是外部类,对外可见,灰色部分是内部类,对外不可见

EventLoop是事件循环的抽象,Poller是I/O复用的抽象,有两个派生类,分别封装poll和epoll,这个地方是muduo库唯一使用面向对象编程风格封装的

EventLoop与Poller的关系是组合,一个EventLoop包含一个Poller,并且Poller的生存期由EventLoop来控制,EventLoop是调用Poller的poll()函数实现的

Channel是对事件的注册与响应的封装,Channel的update()函数负责注册和更新I/O的可读、可写事件,Channel的handleEvent()成员函数对所发生的I/O事件进行处理。当调用update()函数注册和更新I/O的可读、可写事件的时候,又会调用到EventLoop的updateChannel(),从而又调用了Poller的updateChannel(),相当于将Channel注册到Poller中或者将fd_文件描述符的一些可读可写事件注册到Poller中。

一个Eventloop包含多个Channel,也就是说可以用来捕捉多个通道的可读可写事件,两者之间是聚合关系,也就是说EventLoop不负责Channel的生存期的控制。Channel的生存期的控制由TcpConnection、Acceptor、Connector等等这些类来控制

Channel不拥有文件描述符,意思就是当Channel对象销毁的时候不关闭文件描述符。它和文件描述符(不是一个类)的关系是关联关系,一个Channel有一个FileDescriptor,一个EventLoop有多个FileDescriptor,用来监听多个FileDescriptor的相关事件,这个FileDescriptor是被套接字所拥有的,也就是说生存期由套接字来控制,当套接字销毁,文件描述符销毁,套接字类的析构函数调用close()

Channel是TcpConnection、Acceptor、Connector的成员,关系也是组合,生存期由这些类控制,Acceptor是对被动连接的抽象,它关注的是监听套接字的可读事件,监听套接字的可读事件由Channel来注册,然后调用handleEvent(),从而又调用了Acceptor中的handRead(),这就是基于对象的编程思想,回调了handRead(),而不是在Channel中包含一个handRead(),然后由Acceptor继承下来。一旦监听套接字的可读事件(注册由Channel来完成发生,就回调handRead()来响应。Connector的handleWrite()和handleError也是由Channel来注册,Connector()是对主动连接的抽象。一旦被动连接或者主动连接建立之后,就会得到一个已连接套接字,已连接套接字的抽象就是TcpConnection,它会关注一些事件
 

Acceptor的生存期由TcpServer来控制,Connector的生存期由TcpClient来控制。TcpServer与TcpConnection的关系是聚合,一个TcpServer包含多个TcpConnection,但是不控制TcpConnection的生存期,因为有可能客户端关闭了连接,TcpConnection对象也要跟着销毁,而TcpServer对象并不销毁,对于TcpClient也是同样的道理。

时序图

 

EventLoop的loop()函数实际上是调用Poller的poll()函数,可能是PollPoller的poll()函数,也可能是EPollPoller的poll()函数,poll()之后就会返回一些通道activeChannels,也就是检测到通道的一些文件描述符产生了可读事件,然后调用这些activeChannels的handleEvent(),接着调用回调函数。调用把比如TcpConnection或者Acceptor或者Connector里面的回调函数。

enableReading():关注可读事件,把通道注册到EventLoop,从而注册到EventLoop所持有的poll对象中

 

能够poll之前,肯定要注册一些事件,注册事件是由enableReading()或enableWriting()发起的,

void enableReading() { events_ |= kReadEvent; update(); }

它会调用updatae(),进而又调用EventLoop的UpdateChannel(),进而调用Poller的UpdateChannel()

你可能感兴趣的:(muduo源码)