Skip to content
New issue

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

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

Already on GitHub? # to your account

ACK problems? #1

Open
lattice0 opened this issue Jun 21, 2021 · 2 comments
Open

ACK problems? #1

lattice0 opened this issue Jun 21, 2021 · 2 comments

Comments

@lattice0
Copy link

Hi, I've been looking at this repo and I was wondering if you thought of my question about the way you poll each socket. I'd love to use this instead of mine because it does not do a second copy of the rx buffer. But it looks to me that this copy is needed. Look:

impl AsyncRead for TcpSocket {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        let mut set = self.reactor.socket_alloctor().lock();
        let mut socket = set.get::<socket::TcpSocket>(*self.handle);
        if !socket.may_recv() {
            return Poll::Ready(Ok(()));
        }
        if socket.can_recv() {
            let read = socket
                .recv_slice(buf.initialize_unfilled())
                .map_err(map_err)?;
            buf.advance(read);
            return Poll::Ready(Ok(()));
        }
        socket.register_recv_waker(cx.waker());
        Poll::Pending
    }
}

you are saying that there's no chance the socket will be overwritten before a new poll is made. It looks like smoltcp uses arbitrary socket sizes, so if the socket size is of half a TCP packet size, then I thought it could be that half a packet must be taken out of the socket before a new part emerges and then the ACK is sent. However, I think there's no sense in what I said because smoltcp will always analyze the entire packet before delivering to me. So a ACK will only be sent after the entire packet is received.

But is the ACK sent when socket.recv_slice is called? I researched a bit and it looks like not. I think the ACK is sent right after the packet is received in smoltcp and put available to socket.recv_slice. So if I don't call recv_slice fast enough it would be overwritten by the next packet, because ACK is already sent before I call socket.rect_slice. The socket.recv_slice end up calling this:

    pub fn dequeue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
            where F: FnOnce(&'b mut [T]) -> (usize, R) {
        let capacity = self.capacity();
        let max_size = cmp::min(self.len(), capacity - self.read_at);
        let (size, result) = f(&mut self.storage[self.read_at..self.read_at + max_size]);
        assert!(size <= max_size);
        self.read_at = if capacity > 0 {
            (self.read_at + size) % capacity
        } else {
            0
        };
        self.length -= size;
        (size, result)
    }

which does not look like it triggers an ACK response. So it looks like ACK is sent before this.

I'm still not convinced for the TCP case, but what about UDP?

    pub fn poll_recv_from(
        &self,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<(usize, SocketAddr)>> {
        let mut set = self.reactor.socket_alloctor().lock();
        let mut socket = set.get::<socket::UdpSocket>(*self.handle);

        match socket.recv_slice(buf) {
            // the buffer is empty
            Err(smoltcp::Error::Exhausted) => {}
            r => {
                let (size, endpoint) = r.map_err(map_err)?;
                return Poll::Ready(Ok((size, ep2sa(&endpoint))));
            }
        }

        socket.register_recv_waker(cx.waker());
        Poll::Pending
    }

UDP has no ACK, so there's no way that the server would know that I already received a packet, so there's the chance that poll does not occur fast enough to grab a packet before it's overwritten. Same for RawSocket.

By the way, will you support the most recent version of smoltcp? I needed it because of some things from there, mostly to do a virtual interface.

Thanks

@spacemeowx2
Copy link
Owner

Hi, I've thought about polling each socket individually before. And I think it's possible to do that but maybe I can improve it in the future.

About the TCP question:

However, I think there's no sense in what I said because smoltcp will always analyze the entire packet before delivering to me. So a ACK will only be sent after the entire packet is received.

I'm not 100% sure but I believe smoltcp handled it well. There is a assmbler in TcpSocket. So when a TcpPacket comes from the Internet, it copy as much as possible to rx_buffer and notify tokio-smoltcp to get it later.

https://github.com/smoltcp-rs/smoltcp/blob/027f255f904b9b7c4226cfd8b2d31f272ffa5105/src/socket/tcp.rs#L1574-L1585

And send ACK here to tell peer endpoint how much data we have received.

https://github.com/smoltcp-rs/smoltcp/blob/027f255f904b9b7c4226cfd8b2d31f272ffa5105/src/socket/tcp.rs#L1621-L1630

There is window_len to avoid peer endpoint sending oversized packet.

So recv_slice don't make an ACK reply. It just dequeue data from rx_buffer and make rx_buffer avaliable for next poll.

UDP is simpler:

UDP has no ACK, so there's no way that the server would know that I already received a packet

UDP does not provide reliability. If the buffer is full, just drop the packet. Also same for RawSocket

By the way, will you support the most recent version of smoltcp?

I guess I will make a new branch and make smoltcp as git dependency. So you can enjoy the latest feature : )

@spacemeowx2
Copy link
Owner

https://github.com/spacemeowx2/tokio-smoltcp/tree/latest

Not tested yet. PRs are welcome

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

No branches or pull requests

2 participants