0
votes

I'm experiencing some problems with reading TCP packets.

I'm trying to read a JSON response, that's 5000 bytes in size, but looking at the packets in the Wireshark, they're separated into three different packets, first and second being 1448 bytes, and third being 2530 bytes in size.

When I try to read them with Tokio-rs, I only receive the first one, so I don't receive whole JSON data.

For reading, I'm using the following code:

pub async fn read(stream: &mut TcpStream) -> Result<Bytes, std::io::Error>{
    let mut buf = BytesMut::with_capacity(8128);
    let mut resp = [0u8; 8128];
    let buf_len = stream.read(&mut resp).await?;
    buf.extend_from_slice(&resp);
    buf.truncate(buf_len);
    println!("{}", buf.len());
    Ok(buf.freeze())
}

And buf.len() returns 1448 which is exactly the size of the first and second packet, but buf contains data from the first packet.

Now I'm wondering if I missed something and the TcpStream closes with the first received packet or am I missing buffer size somewhere.

1
The results you see are how the Read Trait works, no? You may have to call read multiple times or use something like read_to_end().Jussi Kukkonen
@JussiKukkonen read_to_end() didn't work for me, as it would stop reading when the socket closed, but mine doesn't.MG lolenstine

1 Answers

2
votes

Read methods, like Read::read or AsyncReadExt::read, generally don't guarantee anything about how much data will be consumed on each all. If TcpStream has three packets available, it may consume the first packet only, or the first two packets, or the first packet and half of the second one. Whatever it does is an implementation detail. The only assumption you can make is that if it returns 0 (i.e. no bytes were read), it has reached its "end of stream" (for example because the connection was closed).

Because of this, you should generally read in a loop:

let mut buf = BytesMut::with_capacity(8128);
let mut resp = [0u8; 8128];

loop {
    let buf_len = stream.read(&mut resp).await?;
    buf.extend_from_slice(&resp[0..buf_len]);

    if buf_len == 0 {
      // end of stream
      panic!("Unexpected EOF");
    } else if buf.len() >= 5000 {
      //      ^---------------^
      //               \_________ some condition to check if buffer is "ready"

      // buffer has been filled with enough bytes
      break;
    } else {
      // buffer does not have enough bytes, keep reading...
      continue;
    }
}

println!("{}", buf.len());
Ok(buf.freeze())

Alternatively, if you want to fill up the entire buffer, you can use the read_exact method instead, which will read in a loop for you until the buffer is full, or read_to_end, which will read until end of stream is reached.