-1

is it possible in pure rust to write a single-threaded TCP Server? In C I would use the select syscall to "listen" to multiple sockets. I only find solutions where people use unsafe to use epoll/select, but I really want to avoid this. I think this is a basic task and I cant imagine that there is no pure rust solution to solve such a task. Basically I am looking for an abstraction in the standard library.

Here is what I want in C: https://www.gnu.org/software/libc/manual/html_node/Server-Example.html

E.g. using unsafe with select/epoll: https://www.zupzup.org/epoll-with-rust/index.html

Alai
  • 123
  • 1
  • 8

2 Answers2

1

select and friends are syscalls. To use select and friends, at one point, Rust needs to call those syscalls. Those syscalls are not expressed in Rust, they use C semantics (when calling them via libc), or assembly (when calling them directly).

From the perspective of Rust that means they're unsafe, the Rust compiler has no way to know what they're doing at all.

That means in order to use them from rust you have two choices:

  • call them directly, using unsafe
  • or use a higher-level package which ends up calling them (internally using unsafe), like tokio

Even if there were a (linux-only) pure-rust and rust-targeted reinvention of libc, it would ultimately have to use unsafe in order to craft the actual syscalls and call into the kernel. So would a hypothetical pure-rust bare-metal unikernel.

Masklinn
  • 34,759
  • 3
  • 38
  • 57
  • Sorry for the confusion, I dont want to call select in rust, I want an abstraction in pure rust which behaves the same, I want a single-threaded TCP Server in Rust. Is there a way to do this? – Alai Apr 16 '21 at 23:54
  • 1
    It doesn't matter that you don't want to call select, because at the end of the day whichever "abstraction in pure rust" you use (which again is an evented reactor like tokio & al) will have to call *it*, or epoll, or whatever. So there *will* be unsafe involved at that layer. – Masklinn Apr 17 '21 at 10:23
  • So it is not possible to do this? If I would ask if it is possible to open/read/close a file, you would tell me to use the safe std::fs and you would not point out that these are syscalls. So why is there a problem to just tell me, that it is not possible without using unsafe code? – Alai Apr 17 '21 at 14:23
  • I dont really understand why it should be obvious that there is no safe abstraction for this kind of task in the standard library. – Alai Apr 17 '21 at 14:26
  • I'm not saying there is no safe abstraction, I've literally given you examples of them. – Masklinn Apr 17 '21 at 19:46
  • You said, every abstraction is using unsfe, not that there are any abstractions without calling unsafe, but (according to your messages) since this seams impossible without unsafe, I will use an abstraction using unsafe. Thank you – Alai Apr 17 '21 at 21:03
0

I ended up using async/await. In this example is not proper error handling.

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());
}
Alai
  • 123
  • 1
  • 8