网络类型
前三部分讲的是「一个 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。两章:
- 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的那次修复。 - 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 到挂起。
💬 关联讨论
发起讨论加载中...