Search code examples
rustrust-tokio

How to use different codec on the same TcpStream, as Framed::new() consumes TcpStream?


I am currently working on a chat server using tokio with its utilities. Assume the server receives a connection:

let listener = TcpListener::bind("0.0.0.0:1234").await?;
let (stream, _) = listener.accept().await?;

By doing below, I am able to read or write lines:

let mut lines = Framed::new(stream, LinesCodec::new());
lines.send("write a line to client").await?;
let string_from_client = lines.next().unwrap();

However, if I want to send files, images, etc. I may need to use other codec(BytesCodec), while TcpStream is consumed by the constructor of Framed. How am I able to do that? If there is a solution, how can I drop the TcpStream and disable all Framed(s) after shutdown gracefully?


Solution

  • You can use Framed::into_inner to get the underlying stream back:

    let mut lines = Framed::new(stream, LinesCodec::new());
    lines.send("write a line to client").await?;
    let string_from_client = lines.next().await;
    let stream = lines.into_inner();
    

    Though, be careful, as this drops the internal state of the LinesCodec and, more importantly, this drops the state of the Framed that contains the read- and write-buffers. So if you for example received foo\nbar, read foo, and now call into_inner, you're discarding bar (it's in the read-buffer of the Framed).

    Your use-case sounds like you might want to implement your own Decoder and Encoder. The Decoder could for example decode into a enum { Text(String), Bytes(Bytes) }.

    how can I drop the TcpStream and disable all Framed(s) after shutdown gracefully?

    When dropping the Framed, you're also dropping the TcpStream (it's contained inside the Framed).