// +build linux darwin dragonfly freebsd netbsd openbsd rumprun // Package reuseport provides TCP net.Listener with SO_REUSEPORT support. // // SO_REUSEPORT allows linear scaling server performance on multi-CPU servers. // See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ for more details :) // // The package is based on https://github.com/kavu/go_reuseport . package reuseport import ( "errors" "fmt" "net" "os" "syscall" ) func getSockaddr(network, addr string) (sa syscall.Sockaddr, soType int, err error) { // TODO: add support for tcp and tcp6 networks. if network != "tcp4" { return nil, -1, errors.New("only tcp4 network is supported") } tcpAddr, err := net.ResolveTCPAddr(network, addr) if err != nil { return nil, -1, err } var sa4 syscall.SockaddrInet4 sa4.Port = tcpAddr.Port copy(sa4.Addr[:], tcpAddr.IP.To4()) return &sa4, syscall.AF_INET, nil } // ErrNoReusePort is returned if the OS doesn't support SO_REUSEPORT. type ErrNoReusePort struct { err error } // Error implements error interface. func (e *ErrNoReusePort) Error() string { return fmt.Sprintf("The OS doesn't support SO_REUSEPORT: %s", e.err) } // Listen returns TCP listener with SO_REUSEPORT option set. // // Only tcp4 network is supported. // // ErrNoReusePort error is returned if the system doesn't support SO_REUSEPORT. func Listen(network, addr string) (l net.Listener, err error) { var ( soType, fd int file *os.File sockaddr syscall.Sockaddr ) if sockaddr, soType, err = getSockaddr(network, addr); err != nil { return nil, err } syscall.ForkLock.RLock() fd, err = syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) if err == nil { syscall.CloseOnExec(fd) } syscall.ForkLock.RUnlock() if err != nil { return nil, err } if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { syscall.Close(fd) return nil, err } if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil { syscall.Close(fd) return nil, &ErrNoReusePort{err} } if err = syscall.Bind(fd, sockaddr); err != nil { syscall.Close(fd) return nil, err } if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil { syscall.Close(fd) return nil, err } name := fmt.Sprintf("reuseport.%d.%s.%s", os.Getpid(), network, addr) file = os.NewFile(uintptr(fd), name) if l, err = net.FileListener(file); err != nil { file.Close() return nil, err } if err = file.Close(); err != nil { l.Close() return nil, err } return l, err }