Rust in Everything
基于 mio 1.2.4

网络类型

前三部分讲的是「一个 fd 怎么注册、事件怎么回来」。这一部分讲 fd 本身从哪里来:mio::net 和 mio::unix::pipe 里的类型怎么建出一个非阻塞的 socket 或 pipe,读写怎么转给 std。代码在 src/net/(约 2.3k 行,大半是文档)和 src/sys/unix/ 下的 net.rs、tcp.rs、udp.rs、uds/、pipe.rs。

这些类型的结构都一样:一个 inner: IoSource<std 类型> 字段。mio 自己写的只有三类代码:建 socket(一次 socket(2) 带上 SOCK_NONBLOCK | SOCK_CLOEXEC,macOS 上改用 fcntl)、accept 和 TCP 的非阻塞 connect;其余的读写、选项、地址都转给 std。两章:

  1. TCP:TcpListener::bind 的四个系统调用(SO_REUSEADDR、listen(128),backlog 从 1024 改到 -1 又改到 128 的经过);accept 在 Linux 上是 accept4,在 macOS 上靠继承;TcpStream::connect 立刻返回,连上还是被拒要等可写事件后用 take_error 和 peer_addr 判断,两个平台上成功、被拒各跑一遍;from_std 不会替你设非阻塞,不设时事件循环卡在 recvfrom 里;set_nodelay 等选项经 Deref 直接用 std,不依赖 socket2;peek 漏掉 do_io 的那次修复。
  2. UDP 与 Unix 域套接字:UdpSocket 的 send_to / recv_from、connect 后的 send / recv / peek,一个事件对应多个数据报,连到关闭端口时 Linux 只报 EPOLLERR;UnixListener / UnixStream / UnixDatagram 的地址处理,1.0 换成 std 的 SocketAddr,macOS 上 accept 返回的「无名地址」怎么处理,connect 碰到满的 backlog 时 Linux 返回 WouldBlock;pipe::new 的 pipe2,以及从子进程拿到的阻塞 pipe 要先 set_nonblocking。
flowchart TB
  ctor["bind / connect / pair / pipe::new"] --> sock["非阻塞 + CLOEXEC 的 fd"]
  sock --> iosrc["IoSource 包住 std 类型"]
  iosrc --> regnode["register:注册主线"]
  iosrc --> io["读写:do_io 转给 std"]
  io --> wb["WouldBlock:回到 poll"]
  fromstd["from_std"] -->|"调用方设非阻塞"| iosrc

IoSource 和 do_io 本身在 Source 与 IoSource;accept 循环、读写到 EAGAIN 的完整 strace 在 一个事件循环。tokio 的 TcpStream 等类型把这些 mio 类型包进 PollEvented,见 tokio 卷的 从 TcpStream::read 到挂起。

💬 关联讨论

发起讨论
加载中...