tokio-rs / tokio-uds

Unix Domain Sockets for tokio
Apache License 2.0
52 stars 25 forks source link

Example of reading from a Unix socket #9

Closed joerg-krause closed 7 years ago

joerg-krause commented 7 years ago

First, I am a Rust newbie...

I am trying to read data from a Unix socket, but I am failing:

extern crate futures;
extern crate tokio_core;
extern crate tokio_signal;
extern crate tokio_uds;

use std::io::Read;

use futures::{Future, Async, Poll};
use tokio_core::reactor::Core;
use tokio_uds::{UnixListener, UnixStream};

struct Echo {
    listener: UnixListener,
}

impl Future for Echo {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            if let Async::Ready(()) = self.listener.poll_read() {
                println!("New Connection");

                let (mut stream, _) = self.listener.accept().unwrap();
                let mut buffer = Vec::new();
                stream.read_to_end(&mut buffer);
                println!("{:?}", buffer);
            }
        }
    }
}

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let listener = UnixListener::bind("/var/run/hello.sock", &handle).unwrap();

    core.run(Echo {listener: listener}).unwrap()
}

Can somebody provide an example of howto get this working, please? Thanks!

alexcrichton commented 7 years ago

Perhaps tokio-core's example echo program can help? You can just replace TCP with Unix in this case

joerg-krause commented 7 years ago

Thanks @alexcrichton ! I am trying to use it similar to the example (just without an echo):

extern crate futures;
extern crate tokio_core;
extern crate tokio_uds;

use futures::Stream;
use tokio_core::reactor::Core;
use tokio_uds::{UnixListener, UnixStream};

use std::io::Read;

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let ctl = UnixListener::bind("/var/run/hello.sock", &handle).unwrap();
    let incoming = ctl.incoming();

    let done = incoming.for_each(|(mut socket, addr): (UnixStream, _)| {
        println!("New Connection: {:?}", addr);

        // Read the stream
        let mut hello = String::new();
        socket.read_to_string(&mut hello).unwrap();

        Ok(())
    });

    core.run(done).unwrap();
}

Unfortunately, this fails at runtime when I send some data to the socket with the error message: "Resource temporarily unavailable".

I guess I am doing it wrong, but the UnixListener is different than the TcpListener.

alexcrichton commented 7 years ago

@joerg-krause yes the problem is that socket is nonblocking, so read_to_string will fail because it needs to block waiting for data. You may wish to try out the various combinators in tokio_core::io for now.

In general though all TCP examples should suffice as UDS examples

joerg-krause commented 7 years ago

Finally, I got a working example :smile:

extern crate futures;
extern crate tokio_core;
extern crate tokio_uds;

use std::fs;
use std::str;

use futures::{Future, Stream};
use tokio_core::io::read_to_end;
use tokio_core::reactor::Core;
use tokio_uds::UnixListener;

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    static PATH: &'static str = "/var/run/hello.sock";

    let listener = match UnixListener::bind(PATH, &handle) {
        Ok(m) => m,
        Err(_) => {
            fs::remove_file(PATH).unwrap();
            UnixListener::bind(PATH, &handle).unwrap()
        }
    };

    let task = listener.incoming().for_each(|(socket, _)| {
        let buf = Vec::new();
        let reader = read_to_end(socket, buf).map(|(_, _buf)| {
            println!("incoming: {:?}", str::from_utf8(&_buf).unwrap());
        }).then(|_| Ok(()));
        handle.spawn(reader);
        Ok(())
    });

    core.run(task).unwrap()
}
alexcrichton commented 7 years ago

Great!