Good day.
I wrote for tests the simplest UDP server on Go. I launch one copy - everything is fine, it catches broadcast packages, it works. But! When you start the second instance of the application, it gives a listen udp 0.0.0.0:10001: bind: address already in use error to listen udp 0.0.0.0:10001: bind: address already in use .
Googling and climbing the source code of the net package, I found out that SO_REUSEADDR is installed and everything should work well, but it was not there.
Tell me what am I doing wrong?
Listing:
package main import ( "fmt" "net" "os" ) /* A Simple function to verify error */ func CheckError(err error) { if err != nil { fmt.Println("Error: ", err) os.Exit(0) } } func main() { /* Lets prepare a address at any address at port 10001*/ ServerAddr, err := net.ResolveUDPAddr("udp", "0.0.0.0:10001") CheckError(err) /* Now listen at selected port */ ServerConn, err := net.ListenUDP("udp", ServerAddr) CheckError(err) defer ServerConn.Close() buf := make([]byte, 1024) for { n, addr, err := ServerConn.ReadFromUDP(buf) fmt.Println("Received ", string(buf[0:n]), " from ", addr) if err != nil { fmt.Println("Error: ", err) } } } go version:
go version go1.6.2 linux/amd64 lsb_release -a:
No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.2 LTS Release: 16.04 Codename: xenial uname -a:
Linux home-PC 4.4.0-83-generic #106-Ubuntu SMP Mon Jun 26 17:54:43 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux UPD: A little pohamaniv, I found that it is possible to define the variable SO_REUSEPORT and on my Linux it is equal to 15 (so-so solution, but I don’t see any more options). I determined, changed the example to this:
package main import ( "fmt" "log" "os" "syscall" "time" ) const ( SO_REUSEPORT int = 15 ) func main() { s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0) if err != nil { log.Fatal(err) } syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) syscall.SetsockoptInt(s, syscall.SOL_SOCKET, SO_REUSEPORT, 1) // syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) // remove this line when you run linux lsa := &syscall.SockaddrInet4{Port: 12345, Addr: [4]byte{0, 0, 0, 0}} err = syscall.Bind(s, lsa) if err != nil { log.Fatal(err) } syscall.SetNonblock(s, true) rsa := &syscall.SockaddrInet4{Port: 12345, Addr: [4]byte{0, 0, 0, 0}} fin, ack := make(chan bool), make(chan bool) go reader(s, fin, ack) for i := 1; i <= 30; i++ { time.Sleep(time.Second) syscall.Sendto(s, []byte(fmt.Sprintf("%v (%.02d)", os.Getpid(), i)), 0, rsa) } fin <- true <-ack fmt.Println("\nbye then") } func reader(s int, fin <-chan bool, ack chan<- bool) { rb := make([]byte, 32) for { n, _, err := syscall.Recvfrom(s, rb, 0) // n, _, err := syscall.Recvfrom(s, rb, syscall.MSG_PEEK) if err != nil { time.Sleep(time.Second / 1000000) } else { fmt.Print(string(rb[:n]), "\n") } select { case <-fin: ack <- true return default: } } } Result: two instances of the application are successfully bundled to the same port. I looked closely at the output and found that the last instance launched captures the socket for reading. It turns out that no one except him can read from the socket anymore.
Again the question: What am I missing?
SO_REUSEADDRis not about that. - Ainar-G