muduo学习笔记(一)
这两晚读了一下 muduo的代码,从最简单的EchoServer切入,结合 muduo作者的博客,理清类之间的关系,写了一下阅读心得。
主要从中学习几点:
1. 服务器端网络编程(结合Unix网络编程)
非阻塞IO,IO多路复用,多线程。
2. boost库的常用用法(结合 Beyond the C++ Standard Library: An Introduction to Boost)
应用层提供逻辑(Service)注册callback到通信层,使用boost::bind和boost::function。
3. 基于C++的程序设计 (C++ coding standard: 101)
设计风格,编程风格,编程规范
对代码的理解:
EchoServer:
提供事件处理逻辑,供TcpServer回调,如连接建立、断开,消息收发等
TcpServer:
1. 接受连接、断开连接
管理TcpConnection:创建、持有、删除TcpConnection
2. 收发数据
委托TcpConnection
3. 使1、2持续不断的进行
委托EventLoop。接受新连接时,创建TcpConnection对象,从线程池中取出线程并处理。
4. 使1、2能并发的执行
ThreadPool
5. 连接建立、断开,数据接收后,能交由应用层处理
应用层提供处理逻辑,并注册callback
EventLoop:
1. 使Server持续运转;与TcpConnection交互
2. 使用poll/epoll等IO多路复用等待事件发生
3. 为使线程阻塞在poll/epoll上时,能及时唤醒,引入了eventfd。向eventfd写入数据,能使poll/epoll很快返回。
4. 为保证线程安全,在EventLoop::loop()里的操作,必须都在EventLoop所在的线程中执行,因此需要操作投递,即runInLoop/queueInLoop两个函数的功能。
5. 同时提供了定时操作,用timerfd实现。
6. Server主线程的EventLoop: 负责newConnection/removeConnection,即listen & accept
IO线程的EventLoop:负责read/write。如果没有IO线程,则由主线程负责IO。
EventLoopThreadPool:
1. 管理IO线程: std::vector<EventLoopThread*>, std::vector<EventLoop*>
2. 新连接来后,从线程池中取出线程接受连接,进行后续通信。
EventLoopThread:
初始化本线程内的EventLoop,并开始循环。
TcpConnection:
1. send/recv data
2.
3. 在一次send中,如果数据没有全部发送完,剩余的数据将保存在Buffer中,留待下一次发送。下一次发送通过Channel进行:调用Channel的enableWriting, 更新Channel属性(加上write event),并向Poller注册。当IO ready后,EventLoop将处理该次写事件,即Channel::handleEvent.
流程如下:
STEP A:
1. TcpConnection::send() -> sendInLoop()
2. Data remaining?
Yes:append data to outputBuffer_;
Channel::enableWriting() -> add write event && update() -> EventLoop::updateChannel() -> Poller::updateChannel():modify fd's events
No: Channel::disableWriting()
STEP B:
1. Channel::handleEvent() -> writeCallback_, i.e. TcpConnection::handleWrite()
2. Data remaining? Goto A.2
Channel:
通道,fd的对象化?
在网络程序中,涉及各种对象需要与IO交互,如Timer,Connection,Acceptor, Connector。在Linux中,与系统打交道的是文件描述符,fd。Timer等这些对象通过操作fd进行IO,IO多路复用需要管理这些fd。Channel则相当于对fd的代理,方便对fd进行各种操作,如注册、响应事件。Poller维持fd与Channel的映射,对通道进行操作,就会反映到相应的fd上;而fd状态有变化,则会使相应的Channel变成Active状态,促发事件响应、进行实际IO操作。