这是一个使用 Rust 从零开始构建游戏系列的一部分,本次主要构建一个基于io_uring
的异步回显服务器,内容如下:
- 介绍:系列旨在使用 Rust 从零开始构建游戏,目前仅使用标准库,之前创建
io_uring
绑定被视为“作弊”,可在这里找到相关代码仓库。 - 今日目标:基于已学的
io_uring
知识构建异步回显服务器,该服务器为单线程(使用线程进行睡眠操作)、异步(使用io_uring
异步 API)的教育性玩具,目标是让读者理解io_uring
队列工作原理、异步在底层的一种实现方式以及如何基于io_uring
构建回显服务器。 - io_uring 再探:
io_uring
主要由入口(Entry
)、提交队列(Submission queue (SQ)
)和完成队列(Completion queue (CQ)
)三个组件构成。入口有提交队列入口(SQE
)和完成队列入口(CQE
)两种,通过io_uring_get_sqe
获取SQE
,使用对应prep
函数填充SQE
,添加到提交队列后调用io_uring_submit
开始处理,完成后在完成队列中添加CQE
,res
表示操作结果,user_data
用于跟踪队列中的入口。 - 创建服务器:定义
EchoServer
结构体,包含ring
(提交和完成队列)、listener
(监听套接字)、operations
(操作映射)和next_id
(操作 id 计数器)字段,在new
函数中进行初始化。设置io_uring
队列时,创建零初始化的io_uring
实例并调用io_uring_queue_init
初始化,listener
通过TcpListener
创建并设置为非阻塞模式。operations
和next_id
用于关联操作和用户。服务器创建后首先进入run
函数的主应用循环,先添加一个accept
操作开始监听连接,然后循环提交入口到io_uring
处理、窥视完成队列、处理完成或提交新入口,并暂停 1ms 防止过度轮询。 - 操作:需要提交和处理支持的操作,定义了
Operation
枚举和OperationData
结构体,fd
用于保存套接字,operations
哈希映射用于关联id
和操作数据。通过generate_entry_id
函数生成唯一id
并存储在operations
中,解决了提交和处理操作时如何跟踪用户的问题,通过user_data
字段在SQE
和CQE
中关联用户和操作。 - 创建提交:以添加
Receive
操作为例,使用Box::into_raw
创建不安全的buffer
,通过generate_entry_id
生成唯一id
并存储在operations
中,然后创建SQE
并使用set_receive
函数填充,将操作信息写入共享内存。其他操作遵循相同模式,使用不同的prep
函数。 - 处理完成:在操作完成后从完成队列中获取
CQE
,在handle_completion
函数中根据user_data
找到对应的操作数据,根据操作类型调用相应的处理函数,如handle_accept
、handle_receive
和handle_send
,处理函数根据操作结果进行相应处理,如接受新连接、读取或发送数据、关闭连接等,并在适当的时候添加新的操作。 - 总结:整个过程是一个轮询循环,检查操作系统运行的异步 I/O 操作队列,服务器本身不会阻塞等待消息,有轻微的暂停用于队列操作,后续将构建高级的异步运行时来处理 WebSocket 连接。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。