Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Transmission Issue in QUIC Mode with Quinn: Server Receives Empty Message Despite Successful Connection #2038

Open
anchalshivank opened this issue Nov 12, 2024 · 1 comment

Comments

@anchalshivank
Copy link

Problem Overview

I'm implementing a communication layer in Rust that supports both TCP and QUIC protocols for data exchange between a client and a server. I'm using Quinn for the QUIC implementation. My TCP setup works as expected, but I'm encountering issues with the QUIC setup: the server receives an empty message ([]) even though the connection is established successfully.
My Repo

Setup and Execution Commands

I use the following commands to test the TCP and QUIC modes:

  • TCP Listener: cargo run -- listener tcp
  • TCP Client: cargo run -- conn tcp
  • QUIC Listener: cargo run -- listener quic
  • QUIC Client: cargo run -- conn quic

Observed Behavior

  1. TCP Mode: Works fine; the server receives the data sent by the client.
  2. QUIC Mode: The server accepts the connection from the client, but it consistently receives an empty message ([]) instead of the expected data.

Example output for the QUIC listener:

QUIC server started on 127.0.0.1:5000
Running as quic listener on 127.0.0.1:5000
Connection received from 127.0.0.1:6000
Received from client: []

Relevant Code Snippets

Trait Definitions

#[async_trait::async_trait]
pub trait Conn {
    async fn send(&mut self, data: &[u8]) -> Result<(), Box<dyn Error + Send + Sync>>;
    async fn receive(&mut self) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>>;
}

#[async_trait::async_trait]
pub trait Listener {
    async fn accept(&self) -> Result<Box<dyn Conn + Send + Sync>, Box<dyn Error + Send + Sync>>;
}

QuicListen Implementation

pub struct QuicListen {
    pub(crate) endpoint: Endpoint,
}

#[async_trait::async_trait]
impl Listener for QuicListen {
    async fn accept(&self) -> Result<Box<dyn Conn + Send + Sync>, Box<dyn Error + Send + Sync>> {
        let conn = self.endpoint.accept().await;
        let connection = conn.unwrap().await?;
        println!("Connection received from {:?}", connection.remote_address());

        tokio::spawn(async move {
            if let Ok(mut recv_stream) = connection.accept_uni().await {
                let mut buffer = [0; 1024];
                match recv_stream.read(&mut buffer).await {
                    Ok(Some(n)) => println!("Received message from client: {:?}", &buffer[..n]),
                    Ok(None) => println!("Client closed the stream"),
                    Err(e) => eprintln!("Error receiving message: {:?}", e),
                }
            }
        });

        Ok(Box::new(QuicConn {
            connection,
            send_stream: connection.open_uni().await?,
        }))
    }
}

QuicConn Implementation

pub struct QuicConn {
    pub(crate) connection: QuicConnection,
    pub(crate) send_stream: SendStream,
}

#[async_trait]
impl Conn for QuicConn {
    async fn send(&mut self, data: &[u8]) -> Result<(), Box<dyn Error + Send + Sync>> {
        self.send_stream.write_all(data).await?;
        self.send_stream.finish().await?;
        Ok(())
    }

    async fn receive(&mut self) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
        let mut buffer = vec![0; 1024];
        match self.recv_stream.read(&mut buffer).await? {
            Some(n) => {
                buffer.truncate(n);
                Ok(buffer)
            }
            None => Err("Stream closed".into()),
        }
    }
}

What I Tried

  1. Testing Both Modes: Verified that TCP works fine, while QUIC connects but doesn’t transmit data correctly.
  2. Debugging Send and Finish Calls: Ensured that write_all and finish are called on the client-side send_stream to try and flush data to the server.
  3. Inspecting Server Receive Logic: Added debug logs in the QUIC listener to track whether data is received and to confirm that the connection is successfully established.

What I Expected

I expected the server to receive the same data sent from the client in QUIC mode, as it does in TCP mode. Instead, the server reads an empty array [], suggesting that either the data is not being transmitted or not properly read.

Request for Guidance

I’d appreciate any insights on:

  1. Stream Handling Best Practices in Quinn: Are there specific steps to ensure data is fully transmitted and received over unidirectional streams?
  2. Data Flushing or Timing Requirements for QUIC: Could there be a specific order or method required to ensure data consistency over QUIC?
  3. Troubleshooting Techniques for debugging data transmission issues in Quinn.

Thank you in advance for any guidance!

@Ralith
Copy link
Collaborator

Ralith commented Nov 13, 2024

You seem to be calling read exactly once. read is not obligated to return any particular amount of data on a single call. If you're doing the same in TCP, then even there it only appears to work by coincidence. You might be interested in read_to_end or read_exact.

Also be sure to familiarize yourself with the effects of closing a connection and make sure you are not doing so prematurely or from the wrong endpoint.

As with any widely supported network protocol, Wireshark is a useful debugging tool.

Finally, judging by your invocation of await on finish, you seem to be using an obsolete version of Quinn. Please update to a supported version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants