muduo1

  1. 线程安全
  2. 构造函数, 做到线程安全, 只要不要在构造函数中暴露this指针就好了, 因为this尚在构造期间, 不同的线程在不同时期访问这个不完整的this将引发错误, 使用constructor() + init()的方法可以避免这种隐患
  3. 析构函数, 做到线程安全不容易, 原因是析构函数会破坏mutex, 需要某种手段确保析构期间不会发生竞争或压根就不做析构, 前者困难, 后者使用shared_ptr/weak_ptr来代理资源, 只要存在对资源的引用, 就不做析构
    (关于shared_ptr/weak_ptr, 本书p14解释的很好)

2

面向对象分类难: 本质在于某些物体很难准确分类, 不同的人看法不同, 或者本就不止一个分类适合它.

code

tcp server: ipaddress, port, eventloop

Buffer TcpConnect EventLoop TcpServer TcpClient

Buffer接管read write

InetAddress接管pv4地址

EventLoop接管时间循环, 每个线程一个EventLoop, 负责IO和定时器事件派发 异步唤醒
用TimerQueue作为时间管理, 用Poller作为IOmultiplexing

EventLoopThread启动一个线程, 运行EventLoop::Loop

TcpConnection核心, 封装一次TCP连接, 不发起连接

TcpClient发起连接 重试

TcpServer接收客户连接

内部类

Channel??
Scoket封装一个fd, 在析构是关闭fd, 它是Acceptor TcpConnection的成员, 生命期由后者控制
EventLoop TimerQueue也拥有它的fd, 但是不封装为Socket

SocketOps封装各种Sockets系统调用

Poller是PollPoller和EPollPoller的基类 采用点评触发
是EventLoop的成员, 生命期由后者控制
PollPoller和EpollPoller封装poll和epoll
poll便于调试, 因为poll是上下文无关的, 用strace很容易知道库的行为是否正确

Connector用于发起Tcp连接 是TcpClient的成员

Acceptor用于接收Tcp连接, 是TcpServer的成员

TimerQueue用timerfd实现定时, 用std::map管理Timer

EventLoopThreadPoll创建IO线程池, 用于把TcpConnection分配到某个EventLoop线程上, 是TcpServer的成员

线程模型
one loop per thread + thread pool

思维装换
主动调用 换成 注册一个接收连接的回调, 需要使用的时候, 只管往连接中操作 网络库负责无阻赛的发送

TCP
建立连接, 连接一旦建立, 双方是平等的可以各自手法数据
断开连接, 主动断开(close, shutdown) 被动断开recv返回0
消息到达, 文件描述符可读, 事件
对这个事件的处理方式决定了网络编程的风格
阻塞还是非阻塞 如何分包 等, 应用层的缓冲如何设计等
消息发送完毕, 是指将数据发送到了操作系统的内核缓冲区, 之后由TCP协议栈负责数据的发送与重传, 此时并不代表对方已经收到数据

关于6.4.1后面提及的问题, …

封装socket api

Acceptor: 封装等待sockfd, Channel用于观察sockfd上的readable事件, 并调用回调Acceptor::handleRead()
Acceptor构造函数和listen成员执行TCP传统api步骤, socket bind listen

TcpServer管理accept建立TcpConnection连接, 该类供用户使用, 由用户管理