diff --git a/src/lib/arch/IArchNetwork.h b/src/lib/arch/IArchNetwork.h index 06a073c10..eaa36cae3 100644 --- a/src/lib/arch/IArchNetwork.h +++ b/src/lib/arch/IArchNetwork.h @@ -225,6 +225,12 @@ public: */ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay) = 0; + //! Turn keep-alive on or off on socket + /*! + Set socket to use keep-alive (true) or not (false). + */ + virtual void setKeepAliveOnSocket(ArchSocket, bool keepAlive) = 0; + //! Turn address reuse on or off on socket /*! Allows the address this socket is bound to to be reused while in the diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp index 57d102654..2e731021b 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.cpp +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -419,6 +419,17 @@ bool ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay) return (oflag != 0); } +void ArchNetworkBSD::setKeepAliveOnSocket(ArchSocket s, bool keepAlive) +{ + if (!s) { + throwError(EINVAL); + } + int opt = keepAlive; + if (setsockopt(s->m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&opt), sizeof(opt)) == -1) { + throwError(errno); + } +} + bool ArchNetworkBSD::setReuseAddrOnSocket(ArchSocket s, bool reuse) { assert(s != nullptr); diff --git a/src/lib/arch/unix/ArchNetworkBSD.h b/src/lib/arch/unix/ArchNetworkBSD.h index eef579682..c593ec225 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.h +++ b/src/lib/arch/unix/ArchNetworkBSD.h @@ -86,6 +86,7 @@ public: size_t writeSocket(ArchSocket s, const void *buf, size_t len) override; void throwErrorOnSocket(ArchSocket) override; bool setNoDelayOnSocket(ArchSocket, bool noDelay) override; + void setKeepAliveOnSocket(ArchSocket, bool keepAlive) override; bool setReuseAddrOnSocket(ArchSocket, bool reuse) override; ArchNetAddress newAnyAddr(AddressFamily) override; ArchNetAddress copyAddr(ArchNetAddress) override; diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp index 999a2bc78..50c18c7a8 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.cpp +++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp @@ -605,6 +605,17 @@ bool ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay) return (oflag != 0); } +void ArchNetworkWinsock::setKeepAliveOnSocket(ArchSocket s, bool keepAlive) +{ + if (!s) { + throwError(WSAEINVAL); + } + int opt = keepAlive; + if (setsockopt_winsock(s->m_socket, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt)) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + bool ArchNetworkWinsock::setReuseAddrOnSocket(ArchSocket s, bool reuse) { LOG_ERR("socket re-use not supported on windows"); diff --git a/src/lib/arch/win32/ArchNetworkWinsock.h b/src/lib/arch/win32/ArchNetworkWinsock.h index 7b36f6b6f..8ebdc6881 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.h +++ b/src/lib/arch/win32/ArchNetworkWinsock.h @@ -73,6 +73,7 @@ public: size_t writeSocket(ArchSocket s, const void *buf, size_t len) override; void throwErrorOnSocket(ArchSocket) override; bool setNoDelayOnSocket(ArchSocket, bool noDelay) override; + void setKeepAliveOnSocket(ArchSocket, bool keepAlive) override; bool setReuseAddrOnSocket(ArchSocket, bool reuse) override; ArchNetAddress newAnyAddr(AddressFamily) override; ArchNetAddress copyAddr(ArchNetAddress) override; diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index 8d691a329..b9d70f934 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -283,6 +283,9 @@ void TCPSocket::init() // that should be sent without (much) delay. for example, the // mouse motion messages are much less useful if they're delayed. ARCH->setNoDelayOnSocket(m_socket, true); + + // turn on keep-alive to avoid dead connections from lingering indefinitely + ARCH->setKeepAliveOnSocket(m_socket, true); } catch (const ArchNetworkException &e) { try { ARCH->closeSocket(m_socket);