I am trying to implement AsyncRead
and AsyncWrite
traits for a custom AsyncUtpStream
that wraps a UDP-based uTP-like protocol implemented using tokio::net::UdpSocket
. While my underlying UDP communication is working fine (e.g., sending and receiving packets as expected), I am facing issues in making my AsyncUtpStream
compatible with Rust’s async I/O traits.
Interaction Between Server and Client
I currently have the server and client interacting as expected over UDP using my custom AsyncUtpSocket
. Here is a simple example of their communication:
Server Output:code
UDP server listening on 127.0.0.1:8080...
Received 20 bytes from 127.0.0.1:37776. Presumably a SYN packet.
---> Received SYN packet
Sent 20 bytes back to client.
Accepted connection from 127.0.0.1:37776
Received 5 bytes from 127.0.0.1:37776: "hello"
Received 18 bytes from 127.0.0.1:37776: "my name is shivank"
Client Output:code
UDP client sending packets...
Sent SYN packet to 127.0.0.1:8080
Received SYN-ACK packet
Connection established with the server
Sent 5 bytes to server: "hello"
Sent 18 bytes to server: "my name is shivank"
Relevant Code
Here is the relevant portion of my code:
AsyncUtpSocket
This struct handles UDP communication and provides methods like send_to
and recv_from
for sending/receiving packets asynchronously.
pub async fn send_to(&self, buf: &[u8]) -> io::Result<usize> { /* ... */ }
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { /* ... */ }
AsyncUtpStream
This struct wraps AsyncUtpSocket
and is intended to implement AsyncRead
and AsyncWrite
traits. Below is my current implementation:
impl AsyncRead for AsyncUtpStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>
) -> Poll<Result<()>> {
/**
I do not understand what to write here, as my recv_from is async and this is a sync function.
I have an idea that we have to pin this future and poll it, but I lack understanding of the approach.
*/
}
}
impl AsyncWrite for AsyncUtpStream {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
data: &[u8]
) -> Poll<Result<usize>> {
/**
Todo!()
*/
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Result<()>> {
Poll::Ready(Ok(()))
}
}
The Problem
-
I am struggling with how to implement the
poll_read
method to handle asynchronous packet-based data (viarecv_from
) and properly populate the providedReadBuf
. -
Similarly, I am unsure how to implement
poll_write
for sending data asynchronously over UDP.
What I Need Help With
-
How can I correctly implement
poll_read
to properly handle data from the underlying UDP socket and populate theReadBuf
? -
Are there any patterns or best practices for implementing
AsyncRead
andAsyncWrite
for a protocol built on top of UDP? -
How should
poll_write
handle the asynchronous nature of UDP to ensure proper data delivery?
Additional Information
-
I am using
tokio
andtokio::net::UdpSocket
for async networking. -
The protocol is uTP-like, so it is packet-based rather than stream-based.
-
The
AsyncUtpSocket
already has methods for sending and receiving data (send_to
andrecv_from
) that work as expected.
Any guidance or examples would be appreciated. Let me know if additional details or code snippets are required.
I attempted to implement the poll_read
function for my custom AsyncUtpStream
using Pin
and Box::pin
.
I think you need this part, which handles the async call https://github.com/xoriors/rencfs/blob/921c1a968ccc3298ba476584b5ea1acc1409a791/src/crypto/fs_api/fs.rs#L389