🔗 网络通信
🔗 需要注意的事项
- 通信代码需要保持简洁、轻量级和快速;它会被频繁调用。
- 通信代码需要在其 IO 缓冲方面具有相当的灵活性;理想情况下,我希望 Squid 的通信模型能比较清晰地映射到 Windows 完成端口 IO 以及 *NIX 上出现的任何类似 API。
🔗 我如何看待通信层
- 一种调度网络套接字读写的方法
- 一种填充/清空数据缓冲区的方法
🔗 我希望通信层做什么和不做什么
- 参与流套接字的读/写调度(目前是 comm_read 和 comm_write)
- 尽可能参与 UDP 数据报套接字发送/接收
- 我也希望通信层像现在一样参与延迟读取
- 它还应该负责套接字的创建和销毁;跟踪半关闭套接字等。
- 理论上,代码不应该直接访问 fd_table[] 和 fdc_table[];应该有非常快速的内联方法来实现这一点。
🔗 当前的通信 API
comm_read
comm_fill_immediate
comm_empty_os_read_buffers
comm_has_pending_read_callback
comm_has_pending_read
comm_read_cancel
fdc_open
comm_udp_recvfrom
comm_udp_recv
comm_udp_send
comm_has_incomplete_write
comm_write
comm_local_port
commBind
comm_open
comm_openex
commConnectStart
commSetTimeout
comm_connect_addr
comm_lingering_close
comm_reset_close
comm_close
comm_add_close_handler
comm_remove_close_handler
commSetNonBlocking
commUnsetNonBlocking
comm_init
comm_old_write
comm_old_write_mbuf
ignoreErrno
checkTimeouts
comm_listen
comm_accept_try
comm_accept
commMarkHalfClosed
commIsHalfClosed
commCheckHalfClosed
DeferredRead::DeferredRead()
CommSelectEngine::checkEvents()
🔗 我希望网络通信层看起来是什么样的
- 每个文件描述符应该只有一个待处理的读和写 IO 操作。
- …因此,每个文件描述符应该只有一个待处理的读/写 IO 回调。
- comm_read/comm_write 例程应该使用一个静态分配的、每个 FD 一个的读/写回调结构。这个结构应该有一个 dlink_node 来将它们“链接”在一起,形成一个已完成的回调列表。
- UDP 发送/接收例程应该以回调驱动。
- 缓冲区管理应该由通信层完成,并将已完成缓冲区的引用交给调用者。为什么?
- 我希望通信代码根据需要填充/清空缓冲区;
- 并且如果生产者/消费者希望消耗更少(例如延迟池),那么通信缓冲区就会填满,通信层将停止调度 IO,直到缓冲区接近或变空;
- 这还意味着我们可以减少 IO 调度开销(Steve Wilton 在 epoll 代码中作为优化实现了这一点,谢谢 Steve!)来在 IO 更改发生时调度它们,而不是每次读/写都调度。
- 我希望支持 Windows 完成 IO 和 *NIX 世界未来可能出现的任何奇怪而美妙的机制,以减少额外的复制。
- 最后,它必须保持简单轻量,同时以尽可能快和高效的方式填充和清空缓冲区。
🔗 需要考虑的事情!
- 当我们可以使用 writev() 时,整个“将报头打包到连续内存区域然后一次性写出”的想法。
- 如果平均“Web”对象大小仍然小于 64k,那么我们可以直接在一个 write()(或 writev())中完成所有这些,而无需任何复制。
- 读/写的最佳大小是多少?