How to make UDP-listener non-exclusive?

1,572 views
Skip to first unread message

Ondekoza

unread,
Aug 21, 2012, 9:29:49 AM8/21/12
to golan...@googlegroups.com
I have a function that is listening on a port for incoming UDP packets.
It works fine. Unfortunately it reserves the port exclusively, so that another application
that wants to listen to the same port will fail to start. The code is

    laddr, err_resolve := net.ResolveUDPAddr("IP4", *input_server)
    if err_resolve != nil {
        message("# ResolveUDPAddr() failed.")
        return
    }
    c, err_listen := net.ListenUDP("udp", laddr)
    if err_listen != nil {
        message("ListenUDP() failed.")
        return
    }
    for {
        len_telegram, errA := c.Read(buffer[0:NBUF])
        if errA != nil {
            return
        }
        // do something with buffer
    }
How do I need to modify the setup, so that my go-application
is not blocking other applications?

Regards
Stefan


Ondekoza

unread,
Aug 21, 2012, 1:38:39 PM8/21/12
to golan...@googlegroups.com, swh...@ngmoco.com
Good point.

Question to everybody: How do I call net.setReuseAddr, when it is not exported (not being SetReuseAddr)?




Stefan Schroeder

unread,
Aug 21, 2012, 1:50:53 PM8/21/12
to golan...@googlegroups.com
There obviously has been some discussion about the setReuseAddr function here:

http://codereview.appspot.com/5520057/

But I cannot follow the arguments and cannot see the conclusion...


2012/8/21 Ondekoza <onde...@gmail.com>:

Andrew Falcon

unread,
Aug 21, 2012, 3:57:08 PM8/21/12
to Ondekoza, golan...@googlegroups.com
Just to clarify the problem:  are both programs trying use the address + port at at once, or is one failing after a previous one exits?  If it is the first situation, setting SO_REUSEADDR won't help.

Ondekoza

unread,
Aug 21, 2012, 10:58:26 PM8/21/12
to golan...@googlegroups.com, Ondekoza
Using the highly recommended e-book by Jan Newmarch called 'Network programming with Go'
I re-wrote the section above to use
DialUDP instead of ListenUDP and
ReadFromUDP instead of Read.

Instead of the failure from ListenUDP I now get

Fatal error %!(EXTRA string=listen udp 127.0.0.1:5151: Only one usage of each so
cket address (protocol/network address/port) is normally permitted.)

when starting the go-app when another is already listening. Googling
quickly revealed that the usage-message is not created by Go, but by the OS.

Unfortunately this didn't help me a lot to find a fix...


David Anderson

unread,
Aug 22, 2012, 1:14:49 AM8/22/12
to Ondekoza, golan...@googlegroups.com
In almost all circumstances, only one application can listen on a
given address:port pair. That's how the operating system associates
packets arriving at its network stack with userspace applications. If
you allowed multiple applications to bind to a single port, you'd end
up with a multitude of issues: if you receive a packet, which
application should receive it? If the answer is "all of them", what
happens when all of them reply, given that the program on the other
end likely expects to talk to a single program?

The standard BSD sockets API, adopted by all operating systems I know
of, makes the fundamental assumption that one socket address is owned
by one program. Flags like SO_REUSEADDR only speed up the ability to
reuse an address previously used by another program before it is
theoretically safe to do so (i.e. before the transport protocol has
definitely established that all past connections have been
terminated), but do not allow you to mingle with another process
actively using the address.

The only exception I know of is linux's SO_REUSEPORT, which is an
option that allows multiple processes to listen on the same port, with
semantics designed to facilitate the spreading of incoming load across
multiple processors. This is only useful in a very narrow set of use
cases, and I suspect this is not what you're trying to accomplish.

If you're trying to "listen in" on some other process's activities,
the standard BSD sockets API does not allow you to do this. You'll
need OS-specific network stack hooks to accomplish this.

- Dave

Mikio Hara

unread,
Aug 22, 2012, 8:57:14 AM8/22/12
to Ondekoza, golan...@googlegroups.com
Hi,

On Tue, Aug 21, 2012 at 10:29 PM, Ondekoza <onde...@gmail.com> wrote:

> I have a function that is listening on a port for incoming UDP packets.
> It works fine. Unfortunately it reserves the port exclusively, so that
> another application
> that wants to listen to the same port will fail to start.

It seems like original net package designers did implement
"please don't do that" things as a set of API.

If you can try attached then you'll see:
- first-come-first-serve behavior on BSD variants,
- last-come-first-serve behavior on linux.

-- Mikio

--
package main

import (
"fmt"
"log"
"os"
"syscall"
"time"
)

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,
syscall.SO_REUSEPORT, 1) // remove this line when you run linux
lsa := &syscall.SockaddrInet4{Port: 12345}
err = syscall.Bind(s, lsa)
if err != nil {
log.Fatal(err)
}
syscall.SetNonblock(s, true)
rsa := &syscall.SockaddrInet4{Port: 12345, Addr: [4]byte{127, 0, 0, 1}}
fin, ack := make(chan bool), make(chan bool)
go reader(s, fin, ack)
for i := 0; i < 30; i++ {
time.Sleep(time.Second)
syscall.Sendto(s, []byte(fmt.Sprintf("%v",
os.Getpid())), 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)
if err != nil {
time.Sleep(time.Second)
} else {
// Let's see who is the winner
fmt.Print(string(rb[:n]), " ")
}
select {
case <-fin:
ack <- true
return
default:
}
}
}
Reply all
Reply to author
Forward
0 new messages