Skip to content

Instantly share code, notes, and snippets.

@artyom
Last active July 17, 2020 06:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save artyom/85c53e94c2a6bdbe84eae10f2d7e753a to your computer and use it in GitHub Desktop.
Save artyom/85c53e94c2a6bdbe84eae10f2d7e753a to your computer and use it in GitHub Desktop.
Listener with SO_REUSEPORT for linux
// +build !linux
package socket
import "net"
func Listen(network, address string) (net.Listener, error) { return net.Listen(network, address) }
package socket
import (
"errors"
"net"
"os"
"syscall"
"time"
"golang.org/x/sys/unix"
)
func Listen(network, address string) (net.Listener, error) {
if network != "tcp" {
return nil, errors.New("only tcp network is supported")
}
addr, err := net.ResolveTCPAddr(network, address)
if err != nil {
return nil, err
}
sockaddr, err := tcpAddrToSockaddr(addr)
if err != nil {
return nil, err
}
syscall.ForkLock.Lock()
defer syscall.ForkLock.Unlock()
s, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM|unix.SOCK_CLOEXEC|unix.SOCK_NONBLOCK, 0)
if err != nil {
return nil, err
}
if err := unix.SetsockoptInt(s, unix.SOL_SOCKET, unix.SO_REUSEADDR, 0); err != nil {
unix.Close(s)
return nil, err
}
if err := unix.SetsockoptInt(s, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
unix.Close(s)
return nil, err
}
if err := unix.Bind(s, sockaddr); err != nil {
unix.Close(s)
return nil, err
}
if err := unix.Listen(s, unix.SOMAXCONN); err != nil {
unix.Close(s)
return nil, err
}
f := os.NewFile(uintptr(s), address)
defer f.Close()
ln, err := net.FileListener(f)
if err != nil {
return nil, err
}
if tln, ok := ln.(*net.TCPListener); ok {
return tcpKeepAliveListener{tln}, nil
}
return ln, nil
}
func tcpAddrToSockaddr(a *net.TCPAddr) (unix.Sockaddr, error) {
if a == nil {
return nil, errors.New("nil *net.TCPAddr")
}
if ip := a.IP.To4(); ip != nil {
b := [net.IPv4len]byte{}
copy(b[:], a.IP.To4())
return &unix.SockaddrInet4{Addr: b, Port: a.Port}, nil
}
return nil, errors.New("ipv6 listener not implemented yet")
}
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment