diff --git a/tcp.go b/tcp.go index 03b2203..95c30df 100644 --- a/tcp.go +++ b/tcp.go @@ -22,6 +22,30 @@ var DefaultConnectTimeout = 5 * time.Second var log = logging.Logger("tcp-tpt") +const keepAlivePeriod = 30 * time.Second + +type canKeepAlive interface { + SetKeepAlive(bool) error + SetKeepAlivePeriod(time.Duration) error +} + +var _ canKeepAlive = &net.TCPConn{} + +func tryKeepAlive(conn net.Conn, keepAlive bool) { + keepAliveConn, ok := conn.(canKeepAlive) + if !ok { + log.Errorf("Can't set TCP keepalives.") + return + } + if err := keepAliveConn.SetKeepAlive(keepAlive); err != nil { + log.Errorf("Failed to enable TCP keepalive: %s", err) + return + } + if err := keepAliveConn.SetKeepAlivePeriod(keepAlivePeriod); err != nil { + log.Errorf("Failed set keepalive period: %s", err) + } +} + // try to set linger on the connection, if possible. func tryLinger(conn net.Conn, sec int) { type canLinger interface { @@ -33,17 +57,18 @@ func tryLinger(conn net.Conn, sec int) { } } -type lingerListener struct { +type tcpListener struct { manet.Listener sec int } -func (ll *lingerListener) Accept() (manet.Conn, error) { +func (ll *tcpListener) Accept() (manet.Conn, error) { c, err := ll.Listener.Accept() if err != nil { return nil, err } tryLinger(c, ll.sec) + tryKeepAlive(c, true) return c, nil } @@ -106,6 +131,7 @@ func (t *TcpTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) // linger is 0, connections are _reset_ instead of closed with a FIN. // This means we can immediately reuse the 5-tuple and reconnect. tryLinger(conn, 0) + tryKeepAlive(conn, true) return t.Upgrader.UpgradeOutbound(ctx, t, conn, p) } @@ -127,7 +153,7 @@ func (t *TcpTransport) Listen(laddr ma.Multiaddr) (transport.Listener, error) { if err != nil { return nil, err } - list = &lingerListener{list, 0} + list = &tcpListener{list, 0} return t.Upgrader.UpgradeListener(t, list), nil }