Search code examples
rustrust-tokio

How to detect that socket is not listening for udp


I am trying to send data over udp. It works, however I don't get any errors even if destination socket is not exist. Is there any way to detect that socket is not listening?

I am using this code. Nothing is listening on port 8081, but I don't get errors at all. In contrast, if i do the same from python I get error message "Connection refused".

use tokio::net::UdpSocket;
use std::error::Error;
use std::io;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let socket = UdpSocket::bind("127.0.0.1:8080").await?;

    let dst = "127.0.0.1:8081".parse()?;

    loop {
        socket.writable().await?;

        match socket.try_send_to(&b"hello world"[..], dst) {
            Ok(sent) => {
                println!("sent {} bytes", sent);
            }
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                // Writable false positive.
                println!("{e}");
            }
            Err(e) => return Err(e.into()),
        }
    }

    Ok(())
}

Update: updated code according for the Steffen's comment below. Still no errors on sending udp message to non listening port.


Solution

  • UDP has no real connection like TCP does, i.e. with an initial connection establishment before any data get send. Thus in order to detect if there is some UDP listener at the destination one need to send a packet there and then check if the packet got received.

    UDP has a mechanism for this: if the receiving system gets a UDP packet for an IP,port without a listener it will send an ICMP unreachable back (for IPv4, similar message for IPv6). Assuming that this ICMP packet actually reaches the original sender (i.e. no firewalls blocking it) then the socket will be marked with this error and the error will be delivered on the next socket operation, like another send.

    But, this is only true if the socket is connected, i.e. socket.connect("127.0.0.1:8081") called in your example (and socket.try_send used instead of socket.try_send_to). For unconnected sockets (as in your case) the error will not be delivered since it does not match a specific connection (since there is no connection).

    if i do the same from python I get error message "Connection refused"

    If you use an unconnected socket in Python you also don't get the error. If you use a connected socket you get the error. So likely your python code was not doing "the same" what your rust code did.

    (from comment) In python I did bind to the socket and then sendall.

    sendall has no argument for a destination, so it requires a connected socket. Hence the error was delivered to the connected socket.