Rust:安全的单线程 TCP 服务器
Rust: Safe single-threaded TCP Server
是否可以在纯 Rust 中编写单线程 TCP 服务器?
在 C 中,我会使用 select 系统调用来“监听”多个套接字。
我只找到人们使用 unsafe to use epoll/select 的解决方案,但我真的想避免这种情况。我认为这是一项基本任务,我无法想象没有纯 Rust 解决方案来解决这样的任务。
基本上我在标准库中寻找抽象。
这是我想要的 C 语言:
https://www.gnu.org/software/libc/manual/html_node/Server-Example.html
例如与 select/epoll 一起使用不安全:
https://www.zupzup.org/epoll-with-rust/index.html
select
和朋友是系统调用。要使用 select
和朋友,在某一时刻,Rust 需要调用这些系统调用。这些系统调用没有用 Rust 表达,它们使用 C 语义(当通过 libc 调用它们时)或汇编(当直接调用它们时)。
从 Rust 的角度来看,这意味着它们是不安全的,Rust 编译器根本无法知道它们在做什么。
这意味着为了从生锈中使用它们,您有两个选择:
- 直接调用它们,使用
unsafe
- 或者使用最终调用它们的更高级别的包(内部使用不安全的),比如 tokio
即使存在(linux-仅)纯 Rust 和针对 Rust 的 reinvention libc,它最终还是必须使用 unsafe
为了制作实际的系统调用并调用内核。假设的纯铁锈裸机 unikernel 也是如此。
我最终使用了 async/await。在这个例子中没有正确的错误处理。
use async_std::net::TcpListener;
use async_std::task;
use async_tungstenite::accept_async;
use futures::{
SinkExt,
StreamExt,
};
use std::net::SocketAddr;
use std::str::FromStr;
async fn run() {
let addrs = [
SocketAddr::from_str("0.0.0.0:9001").unwrap(),
SocketAddr::from_str("[::]:9001").unwrap()];
let listener = TcpListener::bind(&addrs[..]).await.unwrap();
listener.incoming().for_each_concurrent(None, |stream| async move {
let mut websocket = accept_async(stream.unwrap()).await.unwrap();
loop {
let msg = websocket.next().await.unwrap().unwrap();
println!("Received {}", msg);
if msg.is_binary() || msg.is_text() {
websocket.send(msg).await.unwrap();
}
}
}).await;
}
fn main () {
task::block_on(run());
}
是否可以在纯 Rust 中编写单线程 TCP 服务器? 在 C 中,我会使用 select 系统调用来“监听”多个套接字。 我只找到人们使用 unsafe to use epoll/select 的解决方案,但我真的想避免这种情况。我认为这是一项基本任务,我无法想象没有纯 Rust 解决方案来解决这样的任务。 基本上我在标准库中寻找抽象。
这是我想要的 C 语言: https://www.gnu.org/software/libc/manual/html_node/Server-Example.html
例如与 select/epoll 一起使用不安全: https://www.zupzup.org/epoll-with-rust/index.html
select
和朋友是系统调用。要使用 select
和朋友,在某一时刻,Rust 需要调用这些系统调用。这些系统调用没有用 Rust 表达,它们使用 C 语义(当通过 libc 调用它们时)或汇编(当直接调用它们时)。
从 Rust 的角度来看,这意味着它们是不安全的,Rust 编译器根本无法知道它们在做什么。
这意味着为了从生锈中使用它们,您有两个选择:
- 直接调用它们,使用
unsafe
- 或者使用最终调用它们的更高级别的包(内部使用不安全的),比如 tokio
即使存在(linux-仅)纯 Rust 和针对 Rust 的 reinvention libc,它最终还是必须使用 unsafe
为了制作实际的系统调用并调用内核。假设的纯铁锈裸机 unikernel 也是如此。
我最终使用了 async/await。在这个例子中没有正确的错误处理。
use async_std::net::TcpListener;
use async_std::task;
use async_tungstenite::accept_async;
use futures::{
SinkExt,
StreamExt,
};
use std::net::SocketAddr;
use std::str::FromStr;
async fn run() {
let addrs = [
SocketAddr::from_str("0.0.0.0:9001").unwrap(),
SocketAddr::from_str("[::]:9001").unwrap()];
let listener = TcpListener::bind(&addrs[..]).await.unwrap();
listener.incoming().for_each_concurrent(None, |stream| async move {
let mut websocket = accept_async(stream.unwrap()).await.unwrap();
loop {
let msg = websocket.next().await.unwrap().unwrap();
println!("Received {}", msg);
if msg.is_binary() || msg.is_text() {
websocket.send(msg).await.unwrap();
}
}
}).await;
}
fn main () {
task::block_on(run());
}