0

Consider the following code:

extern crate tokio; // Executor runtime

use tokio::prelude::*;
use tokio::net::TcpListener;
use std::net::SocketAddr;

fn main() {
    let addr = "127.0.0.1:8118".parse::<SocketAddr>().unwrap();
    let listener = TcpListener::bind(&addr)
        .expect("unable to bind TCP listener");
    tokio::run(listener.incoming()
        .map_err(|e| eprintln!("failed to accept socket; error = {:?}", e))
        .for_each(|mut socket| {
            let mut buf = vec![];
            socket.read_to_end(&mut buf).unwrap();
            println!("Received: {:#?}", buf);
            Ok(())
        })
    );
}

When I run this and send something to port 8118 I get the following error:

thread 'tokio-runtime-worker-0' panicked at 'called `Result::unwrap()` on an `Err` value: Kind(WouldBlock)', src/libcore/result.rs:997:5

I imagine there is some way to put to put my socket in blocking mode, or perhaps catch the error and do something with it. I wonder what the standard, canonical way to approach this problem is.

I'd rather not block, since I want the server to do other things while waiting for clients, so an async / threaded solution would be fantastic.

Daniel Porteous
  • 5,536
  • 3
  • 25
  • 44
  • *some way to put to put my socket in blocking mode* — perhaps you don't want to use Tokio, where the entire purpose of it's existence is to allow asynchronous IO... – Shepmaster Jul 06 '19 at 17:24
  • I don't want the IO to block the rest of the application, so I want async or threading. More concretely, the server should be able to sit there and listen for stuff (later adding it to a channel) while the rest of the server continues to do stuff. – Daniel Porteous Jul 06 '19 at 17:27

1 Answers1

1

You are using Tokio, a library where the entire purpose is to enable asynchronous IO. You never want to perform blocking operations in the asynchronous event loop.

Instead, either go all-in on async or avoid it completely and use simpler, coarser threads.

Tokio's io::read_to_end creates a future that is capable of reading all the data from a socket:

use std::net::SocketAddr;
use tokio::{net::TcpListener, prelude::*}; // 0.1.22

fn main() {
    let addr = "127.0.0.1:8118".parse::<SocketAddr>().unwrap();
    let listener = TcpListener::bind(&addr).expect("unable to bind TCP listener");

    tokio::run(
        listener
            .incoming()
            .and_then(|s| tokio::io::read_to_end(s, vec![]))
            .map_err(|e| panic!("failed: {:?}", e))
            .for_each(|(_socket, buf)| {
                println!("Received: {:#?}", buf);
                Ok(())
            }),
    );
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366