Last active
July 17, 2020 06:29
-
-
Save artyom/85c53e94c2a6bdbe84eae10f2d7e753a to your computer and use it in GitHub Desktop.
Listener with SO_REUSEPORT for linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// +build !linux | |
package socket | |
import "net" | |
func Listen(network, address string) (net.Listener, error) { return net.Listen(network, address) } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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