铲子

这是一个使用 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开始处理,完成后在完成队列中添加CQEres表示操作结果,user_data用于跟踪队列中的入口。
  • 创建服务器:定义EchoServer结构体,包含ring(提交和完成队列)、listener(监听套接字)、operations(操作映射)和next_id(操作 id 计数器)字段,在new函数中进行初始化。设置io_uring队列时,创建零初始化的io_uring实例并调用io_uring_queue_init初始化,listener通过TcpListener创建并设置为非阻塞模式。operationsnext_id用于关联操作和用户。服务器创建后首先进入run函数的主应用循环,先添加一个accept操作开始监听连接,然后循环提交入口到io_uring处理、窥视完成队列、处理完成或提交新入口,并暂停 1ms 防止过度轮询。
  • 操作:需要提交和处理支持的操作,定义了Operation枚举和OperationData结构体,fd用于保存套接字,operations哈希映射用于关联id和操作数据。通过generate_entry_id函数生成唯一id并存储在operations中,解决了提交和处理操作时如何跟踪用户的问题,通过user_data字段在SQECQE中关联用户和操作。
  • 创建提交:以添加Receive操作为例,使用Box::into_raw创建不安全的buffer,通过generate_entry_id生成唯一id并存储在operations中,然后创建SQE并使用set_receive函数填充,将操作信息写入共享内存。其他操作遵循相同模式,使用不同的prep函数。
  • 处理完成:在操作完成后从完成队列中获取CQE,在handle_completion函数中根据user_data找到对应的操作数据,根据操作类型调用相应的处理函数,如handle_accepthandle_receivehandle_send,处理函数根据操作结果进行相应处理,如接受新连接、读取或发送数据、关闭连接等,并在适当的时候添加新的操作。
  • 总结:整个过程是一个轮询循环,检查操作系统运行的异步 I/O 操作队列,服务器本身不会阻塞等待消息,有轻微的暂停用于队列操作,后续将构建高级的异步运行时来处理 WebSocket 连接。
阅读 13
0 条评论