├── LICENSE ├── README.md ├── keepalive.go ├── keepalive_bsd.go ├── keepalive_darwin.go ├── keepalive_linux.go └── keepalive_solaris.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Felix Geisendörfer (felix@debuggable.com) and contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcpkeepalive 2 | 3 | **Known Issues:** Some problems with the implementation were [reported](https://groups.google.com/d/msg/golang-nuts/rRu6ibLNdeI/TIzShZCmbzwJ), I'll try to fix them when I get a chance, or if somebody sends a PR. 4 | 5 | Package tcpkeepalive implements additional TCP keepalive control beyond what is 6 | currently offered by the net pkg. 7 | 8 | Only Linux \>= 2.4, DragonFly, FreeBSD, NetBSD and OS X \>= 10.8 are supported 9 | at this point, but patches for additional platforms are welcome. 10 | 11 | See also: https://felixge.de/2014/08/26/using-tcp-keepalive-with-go/ 12 | 13 | **License:** MIT 14 | 15 | **Docs:** http://godoc.org/github.com/felixge/tcpkeepalive 16 | -------------------------------------------------------------------------------- /keepalive.go: -------------------------------------------------------------------------------- 1 | // Package tcpkeepalive implements additional TCP keepalive control beyond what 2 | // is currently offered by the net pkg. 3 | // 4 | // Only Linux >= 2.4, DragonFly, FreeBSD, NetBSD and OS X >= 10.8 are supported 5 | // at this point, but patches for additional platforms are welcome. 6 | // 7 | // See also: http://felixge.de/2014/08/26/tcp-keepalive-with-golang.html 8 | package tcpkeepalive 9 | 10 | import ( 11 | "fmt" 12 | "net" 13 | "os" 14 | "syscall" 15 | 16 | "time" 17 | ) 18 | 19 | // EnableKeepAlive enables TCP keepalive for the given conn, which must be a 20 | // *tcp.TCPConn. The returned Conn allows overwriting the default keepalive 21 | // parameters used by the operating system. 22 | func EnableKeepAlive(conn net.Conn) (*Conn, error) { 23 | tcp, ok := conn.(*net.TCPConn) 24 | if !ok { 25 | return nil, fmt.Errorf("Bad conn type: %T", conn) 26 | } 27 | if err := tcp.SetKeepAlive(true); err != nil { 28 | return nil, err 29 | } 30 | file, err := tcp.File() 31 | if err != nil { 32 | return nil, err 33 | } 34 | fd := int(file.Fd()) 35 | return &Conn{TCPConn: tcp, fd: fd}, nil 36 | } 37 | 38 | // Conn adds additional TCP keepalive control to a *net.TCPConn. 39 | type Conn struct { 40 | *net.TCPConn 41 | fd int 42 | } 43 | 44 | // SetKeepAliveIdle sets the time (in seconds) the connection needs to remain 45 | // idle before TCP starts sending keepalive probes. 46 | func (c *Conn) SetKeepAliveIdle(d time.Duration) error { 47 | return setIdle(c.fd, secs(d)) 48 | } 49 | 50 | // SetKeepAliveCount sets the maximum number of keepalive probes TCP should 51 | // send before dropping the connection. 52 | func (c *Conn) SetKeepAliveCount(n int) error { 53 | return setCount(c.fd, n) 54 | } 55 | 56 | // SetKeepAliveInterval sets the time (in seconds) between individual keepalive 57 | // probes. 58 | func (c *Conn) SetKeepAliveInterval(d time.Duration) error { 59 | return setInterval(c.fd, secs(d)) 60 | } 61 | 62 | func secs(d time.Duration) int { 63 | d += (time.Second - time.Nanosecond) 64 | return int(d.Seconds()) 65 | } 66 | 67 | // Enable TCP keepalive in non-blocking mode with given settings for 68 | // the connection, which must be a *tcp.TCPConn. 69 | func SetKeepAlive(c net.Conn, idleTime time.Duration, count int, interval time.Duration) (err error) { 70 | 71 | conn, ok := c.(*net.TCPConn) 72 | if !ok { 73 | return fmt.Errorf("Bad connection type: %T", c) 74 | } 75 | 76 | if err := conn.SetKeepAlive(true); err != nil { 77 | return err 78 | } 79 | 80 | var f *os.File 81 | 82 | if f, err = conn.File(); err != nil { 83 | return err 84 | } 85 | defer f.Close() 86 | 87 | fd := int(f.Fd()) 88 | 89 | if err = setIdle(fd, secs(idleTime)); err != nil { 90 | return err 91 | } 92 | 93 | if err = setCount(fd, count); err != nil { 94 | return err 95 | } 96 | 97 | if err = setInterval(fd, secs(interval)); err != nil { 98 | return err 99 | } 100 | 101 | if err = setNonblock(fd); err != nil { 102 | return err 103 | } 104 | 105 | return nil 106 | } 107 | 108 | func setNonblock(fd int) error { 109 | return os.NewSyscallError("setsockopt", syscall.SetNonblock(fd, true)) 110 | 111 | } 112 | -------------------------------------------------------------------------------- /keepalive_bsd.go: -------------------------------------------------------------------------------- 1 | // +build dragonfly freebsd netbsd 2 | 3 | package tcpkeepalive 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | ) 9 | 10 | func setIdle(fd int, secs int) error { 11 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) 12 | } 13 | 14 | func setCount(fd int, n int) error { 15 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, n)) 16 | } 17 | 18 | func setInterval(fd int, secs int) error { 19 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)) 20 | } 21 | -------------------------------------------------------------------------------- /keepalive_darwin.go: -------------------------------------------------------------------------------- 1 | package tcpkeepalive 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | // from netinet/tcp.h (OS X 10.9.4) 9 | const ( 10 | _TCP_KEEPINTVL = 0x101 /* interval between keepalives */ 11 | _TCP_KEEPCNT = 0x102 /* number of keepalives before close */ 12 | ) 13 | 14 | func setIdle(fd int, secs int) error { 15 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)) 16 | } 17 | 18 | func setCount(fd int, n int) error { 19 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, _TCP_KEEPCNT, n)) 20 | } 21 | 22 | func setInterval(fd int, secs int) error { 23 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, _TCP_KEEPINTVL, secs)) 24 | } 25 | -------------------------------------------------------------------------------- /keepalive_linux.go: -------------------------------------------------------------------------------- 1 | package tcpkeepalive 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | func setIdle(fd int, secs int) error { 9 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) 10 | } 11 | 12 | func setCount(fd int, n int) error { 13 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, n)) 14 | } 15 | 16 | func setInterval(fd int, secs int) error { 17 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)) 18 | } 19 | -------------------------------------------------------------------------------- /keepalive_solaris.go: -------------------------------------------------------------------------------- 1 | package tcpkeepalive 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | func setIdle(fd int, secs int) error { 9 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) 10 | } 11 | 12 | func setCount(fd int, n int) error { 13 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, n)) 14 | } 15 | 16 | func setInterval(fd int, secs int) error { 17 | return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)) 18 | } 19 | --------------------------------------------------------------------------------