25

I'm looking for a good solution for a client/server communication with UDP sockets in Go language.

The examples I found on the Internet show me how to send data to the server, but they do not teach how to send them back to the client.

To demonstrate, my program does the following:

My client program creates a socket on the 4444 port, like this:

con, err := net.Dial("udp", "127.0.0.1:4444")

I sent a string and the local address to the server, so it could print the string and send an OK message. I am using gob for this:

enc := gob.NewEncoder(con)
enc.Encode(Data{"test", con.LocalAddr().String()})

My Data struct looks like this:

type Data struct{
    Msg string
    Addr string
}

My server listens to the 4444 port and decodes the Gob correctly, but how can I send the OK message back? I'm using the client address to do so (on the server .go file):

con, err := net.Dial("udp", data.Addr)

Then, I get an error code:

write udp 127.0.0.1:35290: connection refused

When the client tries to connect to the Server's port 4444, the client creates a port with a random number (in this case, 35290) so they can communicate. I know I shouldn't be passing the client's address to the server, but conn.RemoteAddress() does not work. A solution that discovers the client's address would be most appreciated.

Obs.: I know there is ReadFromUDP, so I can read the package. Should I read it, discover the client's address, and send the data to Gob so it can decode it?

Simon Fox
  • 5,995
  • 1
  • 18
  • 22
Aleksandrus
  • 1,589
  • 2
  • 19
  • 31
  • 2
    Read packets using [ReadFromUDP](http://godoc.org/net#UDPConn.ReadFromUDP). Use the address returned from ReadFromUDP to reply using [WriteToUDP](http://godoc.org/net#UDPConn.WriteToUDP). – Simon Fox Sep 25 '14 at 02:38
  • I was thinking about that, but how would Gob fit in this situation? – Aleksandrus Sep 25 '14 at 14:27
  • 3
    Encode the gob to a buffer using `var b bytes.Buffer; err := gob.NewEncoder(&b).Encode(v)` and write b.Bytes() to the connection. Decode the gobs using `err := gob.NewDecoder(bytes.NewReader(p)).Decode(&v)` where p is the data read from the connection. – Simon Fox Sep 25 '14 at 14:33

3 Answers3

45

Check the below samples for client/server communication over UDP. The sendResponse routine is for sending response back to client.

udpclient.go

package main
import (
    "fmt"
    "net"
    "bufio"
)

func main() {
    p :=  make([]byte, 2048)
    conn, err := net.Dial("udp", "127.0.0.1:1234")
    if err != nil {
        fmt.Printf("Some error %v", err)
        return
    }
    fmt.Fprintf(conn, "Hi UDP Server, How are you doing?")
    _, err = bufio.NewReader(conn).Read(p)
    if err == nil {
        fmt.Printf("%s\n", p)
    } else {
        fmt.Printf("Some error %v\n", err)
    }
    conn.Close()
}

udpserver.go

package main
import (
    "fmt" 
    "net"  
)


func sendResponse(conn *net.UDPConn, addr *net.UDPAddr) {
    _,err := conn.WriteToUDP([]byte("From server: Hello I got your message "), addr)
    if err != nil {
        fmt.Printf("Couldn't send response %v", err)
    }
}


func main() {
    p := make([]byte, 2048)
    addr := net.UDPAddr{
        Port: 1234,
        IP: net.ParseIP("127.0.0.1"),
    }
    ser, err := net.ListenUDP("udp", &addr)
    if err != nil {
        fmt.Printf("Some error %v\n", err)
        return
    }
    for {
        _,remoteaddr,err := ser.ReadFromUDP(p)
        fmt.Printf("Read a message from %v %s \n", remoteaddr, p)
        if err !=  nil {
            fmt.Printf("Some error  %v", err)
            continue
        }
        go sendResponse(ser, remoteaddr)
    }
}
wasmup
  • 14,541
  • 6
  • 42
  • 58
Nipun Talukdar
  • 4,975
  • 6
  • 30
  • 42
  • 3
    The line conn.Close() in the udpclient.go it will be better to put in a defered sentence, like: defer conn.Close() – alsotoes Oct 31 '17 at 07:46
  • I'm trying to get this working with server running on a remote box and client running locally. If I change the address in server.go to 0.0.0.0, and the address in client.go to the IP of that server, client returns `Some error read udp 127.0.0.1:58464->127.0.0.1:1234: read: connection refused`. Would someone please suggest a way to get this working? Thank you! – gpanda Sep 22 '18 at 16:37
4

hello_echo.go

 package main                                                                                                          

 import (                                                                                                              
     "bufio"                                                                                                           
     "fmt"                                                                                                             
     "net"                                                                                                             
     "time"                                                                                                            
 )                                                                                                                     

 const proto, addr = "udp", ":8888"                                                                                    

 func main() {  

     go func() {                                                                                                       
         conn, _ := net.ListenPacket(proto, addr)                                                                      
         buf := make([]byte, 1024)                                                                                     
         n, dst, _ := conn.ReadFrom(buf)                                                                               
         fmt.Println("serv recv", string(buf[:n]))                                                                     
         conn.WriteTo(buf, dst)                                                                                        
     }()        

     time.Sleep(1 * time.Second)   

     conn, _ := net.Dial(proto, addr)                                                                                  
     conn.Write([]byte("hello\n"))                                                                                     
     buf, _, _ := bufio.NewReader(conn).ReadLine()                                                                     
     fmt.Println("clnt recv", string(buf))                                                                             
 }                                                                                                                     
sof
  • 9,113
  • 16
  • 57
  • 83
2

The gist is here:

https://gist.github.com/winlinvip/e8665ba888e2fd489ccd5a449fadfa73

server.go

/*
Usage:
    go run server.go
See https://gist.github.com/winlinvip/e8665ba888e2fd489ccd5a449fadfa73
See https://stackoverflow.com/a/70576851/17679565
See https://github.com/ossrs/srs/issues/2843
 */
package main

import (
    "fmt"
    "net"
    "os"
    "strconv"
)

func main() {
    serverPort := 8000
    if len(os.Args) > 1 {
        if v,err := strconv.Atoi(os.Args[1]); err != nil {
            fmt.Printf("Invalid port %v, err %v", os.Args[1], err)
            os.Exit(-1)
        } else {
            serverPort = v
        }
    }

    addr := net.UDPAddr{
        Port: serverPort,
        IP:   net.ParseIP("0.0.0.0"),
    }
    server, err := net.ListenUDP("udp", &addr)
    if err != nil {
        fmt.Printf("Listen err %v\n", err)
        os.Exit(-1)
    }
    fmt.Printf("Listen at %v\n", addr.String())

    for {
        p := make([]byte, 1024)
        nn, raddr, err := server.ReadFromUDP(p)
        if err != nil {
            fmt.Printf("Read err  %v", err)
            continue
        }

        msg := p[:nn]
        fmt.Printf("Received %v %s\n", raddr, msg)

        go func(conn *net.UDPConn, raddr *net.UDPAddr, msg []byte) {
            _, err := conn.WriteToUDP([]byte(fmt.Sprintf("Pong: %s", msg)), raddr)
            if err != nil {
                fmt.Printf("Response err %v", err)
            }
        }(server, raddr, msg)
    }
}

client.go

/*
Usage:
    go run client.go
    go run client.go 101.201.77.240
See https://gist.github.com/winlinvip/e8665ba888e2fd489ccd5a449fadfa73
See https://stackoverflow.com/a/70576851/17679565
See https://github.com/ossrs/srs/issues/2843
*/
package main

import (
    "fmt"
    "net"
    "os"
    "strings"
)

func main() {
    serverEP := "127.0.0.1"
    if len(os.Args) > 1 {
        serverEP = os.Args[1]
    }
    if !strings.Contains(serverEP, ":") {
        serverEP = fmt.Sprintf("%v:8000", serverEP)
    }

    conn, err := net.Dial("udp", serverEP)
    if err != nil {
        fmt.Printf("Dial err %v", err)
        os.Exit(-1)
    }
    defer conn.Close()

    msg := "Hello, UDP server"
    fmt.Printf("Ping: %v\n", msg)
    if _, err = conn.Write([]byte(msg)); err != nil {
        fmt.Printf("Write err %v", err)
        os.Exit(-1)
    }

    p := make([]byte, 1024)
    nn, err := conn.Read(p)
    if err != nil {
        fmt.Printf("Read err %v\n", err)
        os.Exit(-1)
    }

    fmt.Printf("%v\n", string(p[:nn]))
}


Winlin
  • 1,136
  • 6
  • 25