diff --git a/src/NetMQ.Tests/SocketOptionsTests.cs b/src/NetMQ.Tests/SocketOptionsTests.cs index 7713f13f..7b7f9df3 100644 --- a/src/NetMQ.Tests/SocketOptionsTests.cs +++ b/src/NetMQ.Tests/SocketOptionsTests.cs @@ -77,8 +77,8 @@ public void GetAndSetAllProperties() socket.Options.TcpKeepalive = true; Assert.True(socket.Options.TcpKeepalive); -// socket.Options.TcpKeepaliveCnt = 100; -// Assert.Equal(100, socket.Options.TcpKeepaliveCnt); + socket.Options.TcpKeepaliveCnt = 100; + Assert.Equal(100, socket.Options.TcpKeepaliveCnt); socket.Options.TcpKeepaliveIdle = TimeSpan.FromMilliseconds(100); Assert.Equal(TimeSpan.FromMilliseconds(100), socket.Options.TcpKeepaliveIdle); diff --git a/src/NetMQ/Core/Options.cs b/src/NetMQ/Core/Options.cs index a65ff825..5cac3c3d 100644 --- a/src/NetMQ/Core/Options.cs +++ b/src/NetMQ/Core/Options.cs @@ -452,6 +452,10 @@ public void SetSocketOption(ZmqSocketOption option, object? optionValue) TcpKeepalive = tcpKeepalive; break; + case ZmqSocketOption.TcpKeepaliveCnt: + TcpKeepaliveCnt = Get(); + break; + case ZmqSocketOption.DelayAttachOnConnect: DelayAttachOnConnect = Get(); break; @@ -649,6 +653,9 @@ public void SetSocketOption(ZmqSocketOption option, object? optionValue) case ZmqSocketOption.TcpKeepalive: return TcpKeepalive; + case ZmqSocketOption.TcpKeepaliveCnt: + return TcpKeepaliveCnt; + case ZmqSocketOption.DelayAttachOnConnect: return DelayAttachOnConnect; diff --git a/src/NetMQ/Core/Transports/Tcp/TcpConnector.cs b/src/NetMQ/Core/Transports/Tcp/TcpConnector.cs index 5b4c6c21..fb22572f 100755 --- a/src/NetMQ/Core/Transports/Tcp/TcpConnector.cs +++ b/src/NetMQ/Core/Transports/Tcp/TcpConnector.cs @@ -248,6 +248,14 @@ public void OutCompleted(SocketError socketError, int bytesTransferred) // Set the TCP keep-alive option values to the underlying socket. m_s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, m_options.TcpKeepalive); +#if NET + if (m_options.TcpKeepaliveIdle != -1) + m_s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, m_options.TcpKeepaliveIdle / 1000); + if (m_options.TcpKeepaliveIntvl != -1) + m_s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, m_options.TcpKeepaliveIntvl / 1000); + if (m_options.TcpKeepaliveCnt != -1) + m_s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, m_options.TcpKeepaliveCnt); +#else if (m_options.TcpKeepaliveIdle != -1 && m_options.TcpKeepaliveIntvl != -1) { // Write the TCP keep-alive options to a byte-array, to feed to the IOControl method.. @@ -259,16 +267,9 @@ public void OutCompleted(SocketError socketError, int bytesTransferred) bytes.PutInteger(endian, m_options.TcpKeepaliveIdle, 4); bytes.PutInteger(endian, m_options.TcpKeepaliveIntvl, 8); -#if NET - if (!OperatingSystem.IsWindows()) - { - throw new InvalidOperationException("Not supported on you platform"); // There is a pull request for .net8.0 - - } -#endif m_s.IOControl(IOControlCode.KeepAliveValues, (byte[])bytes, null); - } +#endif } // Create the engine object for this connection. diff --git a/src/NetMQ/Core/Transports/Tcp/TcpListener.cs b/src/NetMQ/Core/Transports/Tcp/TcpListener.cs index 6a8b02ac..99a37288 100644 --- a/src/NetMQ/Core/Transports/Tcp/TcpListener.cs +++ b/src/NetMQ/Core/Transports/Tcp/TcpListener.cs @@ -204,6 +204,14 @@ public void InCompleted(SocketError socketError, int bytesTransferred) if (m_options.TcpKeepalive != -1) { acceptedSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, m_options.TcpKeepalive); +#if NET + if (m_options.TcpKeepaliveIdle != -1) + acceptedSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, m_options.TcpKeepaliveIdle / 1000); + if (m_options.TcpKeepaliveIntvl != -1) + acceptedSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, m_options.TcpKeepaliveIntvl / 1000); + if (m_options.TcpKeepaliveCnt != -1) + acceptedSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, m_options.TcpKeepaliveCnt); +#else if (m_options.TcpKeepaliveIdle != -1 && m_options.TcpKeepaliveIntvl != -1) { @@ -214,15 +222,11 @@ public void InCompleted(SocketError socketError, int bytesTransferred) bytes.PutInteger(endian, m_options.TcpKeepalive, 0); bytes.PutInteger(endian, m_options.TcpKeepaliveIdle, 4); bytes.PutInteger(endian, m_options.TcpKeepaliveIntvl, 8); -#if NET - if (!OperatingSystem.IsWindows()) - { - throw new InvalidOperationException("Not supported on you platform"); // There is a pull request for .net8.0 - } -#endif + acceptedSocket.IOControl(IOControlCode.KeepAliveValues, (byte[])bytes, null); } +#endif } // Create the engine object for this connection. diff --git a/src/NetMQ/Core/ZmqSocketOption.cs b/src/NetMQ/Core/ZmqSocketOption.cs index e9dd5fad..43b8aab4 100644 --- a/src/NetMQ/Core/ZmqSocketOption.cs +++ b/src/NetMQ/Core/ZmqSocketOption.cs @@ -197,6 +197,15 @@ internal enum ZmqSocketOption /// TcpKeepalive = 34, + /// + /// The maximum number of keepalive probes TCP should send before dropping the connection. + /// + /// + /// This setting controls how many unacknowledged probes are sent before the connection is considered dead. + /// A value of -1 (the default) means to use the OS default setting. + /// + TcpKeepaliveCnt = 35, + /// /// The keep-alive time - the duration between two keepalive transmissions in idle condition. /// diff --git a/src/NetMQ/SocketOptions.cs b/src/NetMQ/SocketOptions.cs index 9ac711c7..de520e68 100644 --- a/src/NetMQ/SocketOptions.cs +++ b/src/NetMQ/SocketOptions.cs @@ -283,6 +283,21 @@ public bool TcpKeepalive // See http://api.zeromq.org/3-2:zmq-getsockopt } + + /// + /// Get or set the maximum number of TCP keepalive probes to send before dropping the connection. + /// + /// + /// This value determines how many unacknowledged keepalive probes TCP should send before assuming the connection is dead. + /// A lower value means faster detection of dead peers, while a higher value allows more tolerance for temporary disruptions. + /// A value of -1 (the default) means to use the OS default setting. + /// + public int TcpKeepaliveCnt + { + get => m_socket.GetSocketOption(ZmqSocketOption.TcpKeepaliveCnt); + set => m_socket.SetSocketOption(ZmqSocketOption.TcpKeepaliveCnt, value); + } + /// /// Get or set the keep-alive time - the duration between two keepalive transmissions in idle condition. /// The TCP keepalive period is required by socket implementers to be configurable and by default is