Troubleshooting a UDP communication problem

Posted by PDXDesigner on Mon, 09 Dec 2019 08:37:47 +0100

Communication model

(a -- > B -- > A): a sends data to B through UDP (a can be a specified destination, or a broadcast message to b). B sends back a message to a according to the source address and port after receiving the message, which is such a simple communication process.

For the explanation of golang udp, please refer to the following https://colobu.com/2016/10/19/Go-UDP-Programming/ This document is very detailed. When the udp socket is connected, when it is unconnected, when it is read/write, and when it is read from UDP / writeto UDP are all explained. They are well written.

code demo

Server:

func main() {
    // Create monitoring
    sock, err := net.ListenUDP("udp4", &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 9000,
    })
    if err != nil {
        fmt.Println("listen udp failure!", err)
        return
    }
    defer sock.Close()

    for {
        // Read data
        data := make([]byte, 4096)
        readNum, rAddr, err := sock.ReadFromUDP(data)
        if err != nil {
            fmt.Println("read data failure!", err)
            continue
        }
        fmt.Println("read byte number:",readNum, "remote addr:", rAddr)
        fmt.Println("received: ", string(data[:readNum]))

        // send data
        sendBts := []byte("hello client!")
        _, err = sock.WriteToUDP(sendBts, rAddr)
        if err != nil {
            fmt.Println("send msg back failure!", err)
            return
        } else {
            fmt.Println("send msg mack ok.", string(sendBts), "to:", rAddr)
        }
    }
}

Client

func main() {
    sock, err := net.DialUDP("udp", nil, &net.UDPAddr{
        IP:   net.IPv4(255, 255, 255, 255), // Broadcast address
        Port: 9000,
    })
    // Create monitoring
    //soct, err := net.ListenUDP("udp4", &net.UDPAddr{
    //    IP:   net.IPv4(0, 0, 0, 0),
    //    Port: 9001,
    //})
    if err != nil {
        fmt.Println("connect udp failure!", err)
        return
    }
    defer sock.Close()

    // send data
    sendBts := []byte("hello server!")
    _, err = sock.Write(sendBts)
    if err != nil {
        fmt.Println("send msg failure!", err)
        return
    }

    // receive data
    data := make([]byte, 4096)
    for {
        readNum, rAddr, err := sock.ReadFromUDP(data)
        if err != nil {
            fmt.Println("read udp failure!", err)
            return
        }
        fmt.Println("read byte number:", readNum, "from:", rAddr)
        fmt.Println("received: ", string(data[:readNum]))
    }
}

Problem presentation

Run the demo above:
Server side

read byte number: 13 remote addr: 10.200.2.50:54404
received:  hello server!
send msg mack ok. hello client! to: 10.200.2.50:54404

Client

// nothing

We found that the client didn't output anything, so we have a problem. The server clearly sent the data, and also specified the destination ip and port number! Why didn't the client receive it? When dial ing on the client side, it will not listen to the local port when sending broadcast messages?? Or is it not the same network card that listens when broadcasting messages? Change the client code with the problem.

// Enable the code commented out by the client
func main() {
    // Create monitoring
    sock, err := net.ListenUDP("udp4", &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 9001, // When testing on the same host, bind different ports with the server; if it is a different host, use the same port
    })
    if err != nil {
        fmt.Println("connect udp failure!", err)
        return
    }
    defer sock.Close()

    // send data
    sendBts := []byte("hello server!")
    _, err = sock.WriteToUDP(sendBts, &net.UDPAddr{
        IP:   net.IPv4(255, 255, 255, 255),
        Port: 9000,
    })
    if err != nil {
        fmt.Println("send msg failure!", err)
        return
    }

    // receive data
    data := make([]byte, 4096)
    for {
        readNum, rAddr, err := sock.ReadFromUDP(data)
        if err != nil {
            fmt.Println("read udp failure!", err)
            return
        }
        fmt.Println("read byte number:", readNum, "from:", rAddr)
        fmt.Println("received: ", string(data[:readNum]))
    }
}

Run again
Server side

read byte number: 13 remote addr: 10.200.2.50:54404
received:  hello server!
send msg mack ok. hello client! to: 10.200.2.50:54404

Client

received:  hello client!

The result really confirms the conjecture that we can't receive messages without listening. Why didn't dial listen? Remember that c code implicitly binds (see https://stackoverflow.com/questions/54443823/udp-socket-sendto-implicit-bindhttps://www.cnblogs.com/skyfsm/p/6287787.html )Will there be different encapsulation between languages? Let's take a look at the source code of golang.

When you see dialUDP and listenUDP, the parameters in the red box are different, and others can be said to be the same. Continue to look at the internetsocket - > socket method

Note that the code in the red box is for udp listening / binding, but there is a precondition: there is local addr(laddr) and there is no remote addr(raddr).

Then change the DialUDP code of the client

sock, err := net.DialUDP("udp", &net.UDPAddr{
    IP:   net.IPv4(0,0,0,0),
    Port: 9001,
}, nil)

Run the client's code again.

connect udp failure! dial udp 0.0.0.0:9001: missing address

Got an error with missing address, which means DialUDP does not support implicit binding.

summary

When using udp programming, if you simply send data, you can use DialUDP to get the socket handle, and then call write method to send data. If you want to receive messages, whether the client or the server, you must use ListenUDP to listen / bind before receiving messages.

Topics: Go socket Programming network