5

I am trying to implement a TCP client in Rust. I am able to read data coming from the server but I can not manage to send data.

Here is the code that I am working on:

extern crate bytes;
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

use self::bytes::BytesMut;
use self::futures::{Future, Poll, Stream};
use self::tokio_core::net::TcpStream;
use self::tokio_core::reactor::Core;
use self::tokio_io::AsyncRead;
use std::io;

#[derive(Default)]
pub struct TcpClient {}

struct AsWeGetIt<R>(R);

impl<R> Stream for AsWeGetIt<R>
where
    R: AsyncRead,
{
    type Item = BytesMut;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        let mut buf = BytesMut::with_capacity(1000);

        self.0
            .read_buf(&mut buf)
            .map(|async| async.map(|_| Some(buf)))
    }
}

impl TcpClient {
    pub fn new() -> Self {
        Self {}
    }

    pub fn connectToTcpServer(&mut self) -> bool {
        let mut core = Core::new().unwrap();
        let handle = core.handle();

        let address = "127.0.0.1:2323".parse().expect("Unable to parse address");
        let connection = TcpStream::connect(&address, &handle);

        let client = connection
            .and_then(|tcp_stream| {
                AsWeGetIt(tcp_stream).for_each(|buf| {
                    println!("{:?}", buf);
                    Ok(())
                })
            })
            .map_err(|e| eprintln!("Error: {}", e));

        core.run(client).expect("Unable to run the event loop");
        return true;
    }
}

How can I add asynchronous data sending functionality?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Akiner Alkan
  • 6,145
  • 3
  • 32
  • 68

1 Answers1

3

If you want to have two completely independent streams of data on the socket, you can use the split() method on the TcpStream in the current version of Tokio:

let connection = TcpStream::connect(&address);
connection.and_then(|socket| {
    let (rx, tx) = socket.split();
    //Independently use tx/rx for sending/receiving
    return Ok(());
});

After the split, you can use rx (the receiving half) and tx (the sending half) independently. Here is a small example that treats sending and receiving as completely independent. The sender-half simply periodically sends the same message, whereas the receiving half just prints all incomming data:

extern crate futures;
extern crate tokio;

use self::futures::{Future, Poll, Stream};
use self::tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite, Error, ReadHalf};
use tokio::prelude::*;
use tokio::timer::Interval;

//Receiver struct that implements the future trait
//this exclusively handles incomming data and prints it to stdout
struct Receiver {
    rx: ReadHalf<TcpStream>, //receiving half of the socket stream
}
impl Future for Receiver {
    type Item = ();
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut buffer = vec![0u8; 1000]; //reserve 1000 bytes in the receive buffer
                                          //get all data that is available to us at the moment...
        while let Async::Ready(num_bytes_read) = self.rx.poll_read(&mut buffer)? {
            if num_bytes_read == 0 {
                return Ok(Async::Ready(()));
            } //socket closed
            print!("{}", String::from_utf8_lossy(&buffer[..num_bytes_read]));
        }
        return Ok(Async::NotReady);
    }
}

fn main() {
    let address = "127.0.0.1:2323".parse().expect("Unable to parse address");
    let connection = TcpStream::connect(&address);
    //wait for the connection to be established
    let client = connection
        .and_then(|socket| {
            //split the successfully connected socket in half (receive / send)
            let (rx, mut tx) = socket.split();

            //set up a simple sender, that periodically (1sec) sends the same message
            let sender = Interval::new_interval(std::time::Duration::from_millis(1000))
                .for_each(move |_| {
                    //this lambda is invoked once per passed second
                    tx.poll_write(&vec![82, 117, 115, 116, 10]).map_err(|_| {
                        //shut down the timer if an error occured (e.g. socket was closed)
                        tokio::timer::Error::shutdown()
                    })?;
                    return Ok(());
                }).map_err(|e| println!("{}", e));
            //start the sender
            tokio::spawn(sender);

            //start the receiver
            let receiver = Receiver { rx };
            tokio::spawn(receiver.map_err(|e| println!("{}", e)));

            return Ok(());
        }).map_err(|e| println!("{}", e));

    tokio::run(client);
}

For some applications, this is enough. However, often you will have a defined protocol / format on the connection. HTTP connections, for example, always consist of requests and responses, each of which consists of a header and the body. Instead of directly working on the byte level, Tokio offers the traits Encoder and Decoder you fit onto a socket, which decodes your protocol and directly gives you the entities you want to work with. For an example you can either look at the very basic HTTP implementation or the line-based codec.

It gets a bit more complicated when an incoming message triggers an outgoing message. For the simplest case (every incoming message leads to exactly one outgoing message) you can have a look at this official request / response example.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Markus Ebner
  • 148
  • 9