libp2p / go-reuseport Goto Github PK
View Code? Open in Web Editor NEWreuse tcp/udp ports in golang
License: ISC License
reuse tcp/udp ports in golang
License: ISC License
It's already called by go when we construct the FileConn.
In Go, the net.Listen function automatically sets the SO_REUSEADDR option by default when creating a listening socket. This ensures that the same address can be used by multiple sockets simultaneously, which is often required in server applications.
the source from go
func (fd *netFD) listenStream(laddr sockaddr, backlog int, ctrlFn func(string, string, syscall.RawConn) error) error {
var err error
if err = setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil {
return err
}
var lsa syscall.Sockaddr
if lsa, err = laddr.sockaddr(fd.family); err != nil {
return err
}
if ctrlFn != nil {
c, err := newRawConn(fd)
if err != nil {
return err
}
if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil {
return err
}
}
if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
return os.NewSyscallError("bind", err)
}
if err = listenFunc(fd.pfd.Sysfd, backlog); err != nil {
return os.NewSyscallError("listen", err)
}
if err = fd.init(); err != nil {
return err
}
lsa, _ = syscall.Getsockname(fd.pfd.Sysfd)
fd.setAddr(fd.addrFunc()(lsa), nil)
return nil
}
// unix
func setDefaultListenerSockopts(s int) error {
// Allow reuse of recently-used addresses.
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
func setDefaultMulticastSockopts(s int) error {
// Allow multicast UDP and raw IP datagram sockets to listen
// concurrently across multiple listeners.
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
I would really, really like to make this module (hack) obsolete.
This requires submitting a good patch to upstream Go. see golang/go#9661
Following example returns an error listen udp 127.0.0.1:1234: address 127.0.0.1:1234: unexpected address type
:
package main
import (
"fmt"
"os"
"github.com/libp2p/go-reuseport"
)
func main() {
l, err := reuseport.Listen("udp", "127.0.0.1:1234")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(l)
}
How do you work around connection resets during a 3 way handshake if the number of listeners changes?
the test.go:
package main
import (
"fmt"
"time"
reuseport "github.com/libp2p/go-reuseport"
)
func main() {
go func() {
// Create a new UDP socket using the reuseport package
conn, err := reuseport.ListenPacket("udp", "0.0.0.0:15443")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer conn.Close()
// Continuously read data from the UDP socket and print it to the console
for {
buf := make([]byte, 1024)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
fmt.Println("Error reading:", err)
continue
}
fmt.Printf("Received %d bytes from %s: %s\n", n, addr.String(), string(buf[:n]))
}
}()
go func() {
// Create a new UDP socket using the reuseport package
conn, err := reuseport.Dial("udp", "0.0.0.0:15443", "192.168.0.1:15443")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
for {
msg := []byte("Hello, world!")
_, err := conn.Write(msg)
if err != nil {
//fmt.Println("Error sending message:", err)
continue
}
//fmt.Println("Message sent:", string(msg))
time.Sleep(1 * time.Second) // Wait for 1 second before sending the next message
}
}()
go func() {
// Create a new UDP socket using the reuseport package
conn, err := reuseport.Dial("udp", "0.0.0.0:15443", "192.168.0.2:15443")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
for {
msg := []byte("Hello, world!")
_, err := conn.Write(msg)
if err != nil {
//fmt.Println("Error sending message:", err)
continue
}
//fmt.Println("Message sent:", string(msg))
time.Sleep(1 * time.Second) // Wait for 1 second before sending the next message
}
}()
time.Sleep(100000 * time.Second)
}
compile the test.go and put it into the 192.168.0.1 and 192.168.0.2, and exec it in the two machines,
but there's no message printed in both of the two machines,
but if i stop one of them and keep the other run,
and run "nc -u -p 15443 192.168.0.1 15443" in the stopped one, I can see many "Hello, world!" printing.
I can't understand why, any hint?
Operating system linux
Looks like constant SO_REUSEPORT has gone from linux impl.
libp2p/go-reuseport/impl_unix.go:47:51: undefined: unix.SO_REUSEPORT
do you plan to develop a new version of TFO to support golang?It is really excellent to reduce the time of TCP three-time handshake ,but don‘t support golang still now,and golang is a popular computer language in recent years。thanks
epoll_ctl_add is safe to call in a different thread, even while another thread (goroutine) is in epoll_wait. Knowing this, we can have a single 'poller' instance that communicates with a master loop. I can work on this if you'd like, although you might be more familiar with the code, let me know.
ref: http://man7.org/linux/man-pages/man2/epoll_wait.2.html#NOTES
First time using the library, I tried doing a Dial()
(only, I'm not using Listen()
) on linux, x86-64, kernel 4.4.0-92-generic but it returns an error protocol not available
from available()
. If I comment out the available()
check then it seems to work as expected, reusing the port as I wanted.
My code looked like this:
var d reuse.Dialer
d.D.Timeout = 5 * time.Second
d.D.LocalAddr, err = net.ResolveTCPAddr("tcp", "0.0.0.0:"+sourcePort)
if err != nil {
return err
}
conn, err := d.Dial("tcp", ipAddress+":"+port)
if err != nil {
return err
}
defer conn.Close()
It's related to #36 and #41 .
I got a compile error when build codes in my linux.
I found related issue in golang official; golang/go#23696
Here is my Ubuntu system. I check go-1.10 and go-1.9.
adm@ubuntu1604:/usr/lib/go-1.10/src$ uname -a
Linux ubuntu1604 4.4.0-128-generic #154-Ubuntu SMP Fri May 25 14:15:18 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
adm@ubuntu1604:/usr/lib/go-1.10/src$ grep -R SO_REUSEPORT * | sort
net/net_test.go: // enabling SO_REUSEPORT option such as Linux,
net/sockopt_bsd.go: return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1))
syscall/net_nacl.go: SO_REUSEPORT
syscall/zerrors_darwin_386.go: SO_REUSEPORT = 0x200
syscall/zerrors_darwin_amd64.go: SO_REUSEPORT = 0x200
syscall/zerrors_darwin_arm64.go: SO_REUSEPORT = 0x200
syscall/zerrors_darwin_arm.go: SO_REUSEPORT = 0x200
syscall/zerrors_dragonfly_amd64.go: SO_REUSEPORT = 0x200
syscall/zerrors_freebsd_386.go: SO_REUSEPORT = 0x200
syscall/zerrors_freebsd_amd64.go: SO_REUSEPORT = 0x200
syscall/zerrors_freebsd_arm.go: SO_REUSEPORT = 0x200
syscall/zerrors_linux_arm64.go: SO_REUSEPORT = 0xf
syscall/zerrors_linux_mips64.go: SO_REUSEPORT = 0x200
syscall/zerrors_linux_mips64le.go: SO_REUSEPORT = 0x200
syscall/zerrors_linux_mips.go: SO_REUSEPORT = 0x200
syscall/zerrors_linux_mipsle.go: SO_REUSEPORT = 0x200
syscall/zerrors_linux_ppc64.go: SO_REUSEPORT = 0xf
syscall/zerrors_linux_ppc64le.go: SO_REUSEPORT = 0xf
syscall/zerrors_linux_s390x.go: SO_REUSEPORT = 0xf
syscall/zerrors_netbsd_386.go: SO_REUSEPORT = 0x200
syscall/zerrors_netbsd_amd64.go: SO_REUSEPORT = 0x200
syscall/zerrors_netbsd_arm.go: SO_REUSEPORT = 0x200
syscall/zerrors_openbsd_386.go: SO_REUSEPORT = 0x200
syscall/zerrors_openbsd_amd64.go: SO_REUSEPORT = 0x200
syscall/zerrors_openbsd_arm.go: SO_REUSEPORT = 0x200
The standard Go syscall package doesn't/won't implement EpollWait on ARM64:
EpollWait returned error: function not implemented. Continuing. linux.go:189
The fix should be simple: replace the syscall
import with golang.org/x/sys/unix
Belated drive-by code review.
It looks like available_unix.go could be replaced with
var (
hasReusePort bool
didReusePort sync.Once
)
func checkReusePort() {
...
hasReusePort = whatever
}
func available() bool {
didReusePort.Do(checkReusePort)
return hasReusePort
}
compilation
on NetBSD fails with
gx/ipfs/QmaaC9QMYTQHCbMq3Ebr3uMaAR2ev4AVqMmsJpgQijAZbJ/go-reuseport/poll
../../../../../gx/ipfs/QmaaC9QMYTQHCbMq3Ebr3uMaAR2ev4AVqMmsJpgQijAZbJ/go-reuseport/poll/poll_bsd.go:29: cannot use nil as type int64 in field value
*** Error code 2
// Available returns whether or not SO_REUSEPORT or equivalent behaviour is
// available in the OS.
func Available() bool {
return true
}
So for every os SO_REUSEPORT is available
But for windows (see below) it does not exist
func Control(network, address string, c syscall.RawConn) (err error) {
controlErr := c.Control(func(fd uintptr) {
err = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)
})
if controlErr != nil {
err = controlErr
}
return
}
do you accept PRs?
The ResolveAddr method could be deprecated from this package. It's primarily used by Dial
which should not be used. It limits the usable networks to those supported arbitrarily by this function. It appears also be used by github.com/sonm-io/core.
possible to provide an example for use with https://github.com/tidwall/redcon?
needing port reuse feature
Building with CGO_ENABLED=0 GOOS=windows
I get the following:
vendor/github.com/libp2p/go-reuseport/interface.go:49: undefined: unix.ENOPROTOOPT
vendor/github.com/libp2p/go-reuseport/interface.go:60: undefined: unix.ENOPROTOOPT
vendor/github.com/libp2p/go-reuseport/interface.go:71: undefined: unix.ENOPROTOOPT
vendor/github.com/libp2p/go-reuseport/interface.go:101: undefined: unix.ENOPROTOOPT
Looks like some unix stuff may be sneaking into the windows build.
If my understanding is correct, SO_REUSEPORT will be the desired behaviour 99.9% of the time. In control_unix.go
, for the following code
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
return
}
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
return
}
instead of setting (or trying to) both options, it should be a try/fail-safe scenario. We should try to set SO_REUSEPORT
and if that fails, try to set SO_REUSEADDR
. The current approach seems a bit redundant. Imo, it should look something like this,
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err == nil {
return
}
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
I tried your demo, but still appeared this problem: "address already in use"
go version go1.4.2 linux/amd64
CentOS release 6.6 (Final)
Linux version 2.6.32-504.el6.x86_64
why?
import (
"fmt"
"log"
"net"
reuseport "github.com/libp2p/go-reuseport"
)
func reuse(options *Options) (net.Conn, string) {
listenAddr := fmt.Sprintf("%s:%s", options.ReuseHost, options.ReusePort)
listener, err := reuseport.Listen("tcp", listenAddr)
if err != nil {
log.Fatalf("[*] Error occured: %s", err.Error()) // HERE throw error
}
}
bind: attempt was made to access a socket in a way forbidden by its access permissions
i tested in 2 different devices which one successfully listen and one fail.
Please check my testcase:
https://github.com/hitzhangjie/codemaster/tree/master/hot_restart
you can go run main.go
to run the server, and curl http://localhost:8080/hello
to see Hello from $pid
.
then kill -SIGUSR1 $pid
to notify it do a fork
, parent and children processes reuseport.Listen successfully.
but curl
always return the same $pid.
Only after I kill -SIGTERM $1st-pid
, the 2nd listener works in the forked children process,
then kill the 2nd forked process, then curl you'll see the 3rd forked process works.
The windows implementation clobbers the error returned from SetsockoptInt with the result of the Control method call.
go-reuseport/control_windows.go
Line 10 in 194b8a8
The unix implementation doesn't handle the error returned from the Control method call at all.
Line 13 in 194b8a8
does not work on ios:
Godeps/_workspace/src/github.com/jbenet/go-reuseport/impl_unix.go:318: undefined: poll.New
It looks like https://github.com/jbenet/go-reuseport/tree/master/poll does not have darwin_armX
or whatever ios in golang expects
When i Listen on ":8080" by udp with reuseport, following error occurs.
$ cat e.go
package main
import (
"fmt"
reuse "github.com/jbenet/go-reuseport"
)
func main() {
_, err := reuse.ListenPacket("udp", ":8080")
fmt.Println(err)
}
$ go run e.go
address family not supported by protocol
This breaks compatibility with golang's standard net.ListenPacket.
In Golang standard, they are guessing address family from network and wildcard address ( http://golang.org/src/net/ipsock_posix.go#L72)
Not sure how much of issue it is
In linux based systems, setting SO_REUSEPORT socket option and binding multiple listening sockets enables kernel to perform connection load balancing onto all listeners.
Whereas this is not implemented in windows and macOS.
But for FreeBSD based systems, an additional socket option called SO_REUSEPORT_LB has to be enabled in order for this load balancing to take place. It is described here.
Hence, this is an enhancement issue to enable this new option for freeBSD based systems.
It works when addr2 := "127.0.0.1:80"
but not work on addr2 := "www.baidu.com:80" which changing to a public address
outputs is
panic: invalid argument
go version: go1.5.1 linux/amd64
Some code like this:
package main
import (
"fmt"
resolve "github.com/jbenet/go-net-resolve-addr"
reuse "github.com/jbenet/go-reuseport"
"net"
)
func main() {
addr0 := "0.0.0.0:8808"
addr1 := "127.0.0.1:8808"
//public address
addr2 := "www.baidu.com:80"
//127.0.0.1:80 works
//addr2 := "127.0.0.1:80"
l1, err := reuse.Listen("tcp", addr0)
if err != nil {
panic(err)
}
go func() {
l1to2bar, err := l1.Accept()
if err == nil {
fmt.Printf("%s accepted conn from %s\n", addrStr(l1.Addr()), addrStr(l1to2bar.RemoteAddr()))
} else {
fmt.Printf(err.Error())
}
}()
a1, err := resolve.ResolveAddr("dial", "tcp", addr1)
if err != nil {
panic(err)
}
d1 := reuse.Dialer{net.Dialer{LocalAddr: a1}}
//dial to a public address
d1to2foo, err := d1.Dial("tcp4", addr2)
if err != nil {
panic(err)
}
fmt.Printf("conn to %s\n", addrStr(d1to2foo.RemoteAddr()))
fmt.Println("IT WORKS!")
}
func addrStr(a net.Addr) string {
return fmt.Sprintf("%s/%s", a.Network(), a)
}
ref ipfs/kubo#2967
on impl_unix.go line:109
if lfamily != rfamily && lprotocol != rfamily {
seems should be
if lfamily != rfamily && lprotocol != rprotocol {
Since d6da3d7, our continuous integration has been failing to go get -t -v ./...
our project, which includes a transitive dependency on this project.
https://travis-ci.org/prysmaticlabs/prysm/jobs/475105707
github.com/libp2p/go-reuseport
# github.com/libp2p/go-reuseport
../../libp2p/go-reuseport/impl_wasm.go:10:53: listen redeclared in this block
previous declaration at ../../libp2p/go-reuseport/impl_unix.go:211:45
../../libp2p/go-reuseport/impl_wasm.go:14:56: listenPacket redeclared in this block
previous declaration at ../../libp2p/go-reuseport/impl_unix.go:291:61
../../libp2p/go-reuseport/impl_wasm.go:18:53: listenStream redeclared in this block
previous declaration at ../../libp2p/go-reuseport/impl_unix.go:260:59
../../libp2p/go-reuseport/impl_wasm.go:22:87: dial redeclared in this block
previous declaration at ../../libp2p/go-reuseport/impl_unix.go:62:87
../../libp2p/go-reuseport/impl_wasm.go:26:18: available redeclared in this block
previous declaration at ../../libp2p/go-reuseport/available_unix.go:23:18
Hi! Could you please describe the use cases for this package? When is the port reusing useful? I would be grateful for any examples or URLs for further reading. Thank you.
So, unfortunately, gx doesn't clean the repo before publishing. The published package currently includes a large exe file.
When I run this command
go get -u github.com/libp2p/go-reuseport
it returns error:
# github.com/libp2p/go-reuseport
../../libp2p/go-reuseport/control_wasm.go:7:58: Control redeclared in this block
previous declaration at ../../libp2p/go-reuseport/control_unix.go:11:58
../../libp2p/go-reuseport/interface.go:33:20: undefined: net.ListenConfig
../../libp2p/go-reuseport/interface.go:41:2: not enough arguments to return
../../libp2p/go-reuseport/interface.go:48:2: not enough arguments to return
../../libp2p/go-reuseport/interface.go:60:10: unknown field 'Control' in struct literal of type net.Dialer
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.