当所有连接都断开时,启用了 KeepAlive 的 C# 套接字不会发送
C# Socket with KeepAlive enabled does not send when all connections are broken
我有一个打开只读 TCP 流套接字的应用程序。我已经设置了 KeepAlive 以确保每秒发送一条消息以检查连接是否仍然正常。 Wireshark 显示 KeepAlive 消息已发送,并且还被另一台计算机确认。我的申请是 运行 windows。
与 network/internet 的其余部分相比,我打开套接字的远程计算机位于单独的 LAN 适配器上。
有两种情况似乎反应方式不同,我想知道为什么。
- 我已连接到互联网。 TCP Keep-Alive 每秒都在变强。我从机器上拔下 LAN 电缆。最后一个 TCP Keep-Alive 不是 ACKd,因此
Socket.Connected
是 false
,正如预期的那样。
- 我没有连接到互联网。 TCP Keep-Alive 每秒都在变强。我从机器上拔下 LAN 电缆。 没有 TCP Keep-Alive 包被发送到远程机器,因此
Socket.Connected
仍然是 true
.
我是否遗漏了一个关于套接字何时发送 TCP Keep-Alive 包的重要概念?例如,如果您根本没有网络连接就不要发送?如果是这样,我如何确保套接字不再 Connected
?
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.SetupKeepAlive();
_socket.Blocking = false;
public static void SetupKeepAlive(this Socket socket, uint interval = 1000U)
{
if (socket is null)
{
throw new ArgumentNullException(nameof(socket));
}
// Get the size of the uint to use to back the byte array
var size = Marshal.SizeOf(0U);
// Create the byte array
var keepAlive = new byte[size * 3];
// Pack the byte array:
// Turn keep-alive on
Buffer.BlockCopy(BitConverter.GetBytes(1U), 0, keepAlive, 0, size);
// Set amount of time without activity before sending a keep-alive
Buffer.BlockCopy(BitConverter.GetBytes(interval), 0, keepAlive, size, size);
// Set keep-alive interval
Buffer.BlockCopy(BitConverter.GetBytes(interval), 0, keepAlive, size * 2, size);
// Set the keep-alive settings on the underlying Socket
_ = socket.IOControl(IOControlCode.KeepAliveValues, keepAlive, null);
}
套接字不知道 Internet 连接的可用性,但它知道网络设备状态。它只是一个非常低级的TCP单元。
它只能发送和接收东西,不能诊断网络设备后面的网络。我没有阅读 RFC,但您描述的行为是预期的。
当 TCP 连接建立时,会定期发送 Keep-alive。
therefore Socket.Connected is still true
看起来像是一个已知的 TCP 队首问题。套接字正在等待响应,直到发生超时被关闭。
想象一下设置:您处于糟糕的连接环境中,某些主机正在丢失数据包或挂起几秒钟。然后,如果 Socket 仅在未收到 keep-alive ping 时才会在超时之前关闭连接,您将获得一个关闭的连接。 Keep-alive 旨在使连接寿命更长而不是更短。
还有一个提示:
var keepAlive = new byte[12];
var values = MemoryMarshal.Cast<byte, int>(ref keepAlive);
values[0] = 1;
values[1] = interval; // make it int
values[2] = interval;
// the values were written directly to byte[] array
您似乎使用了错误的数字大小。数组大小应为 12 字节 (3x4) 而不是 24 字节 (3x8)。可能您将 C++ long
用作 C# long
。但是 C++ long
在 C# 中是 int
。 C# long
是 C++ long long
.
我有一个打开只读 TCP 流套接字的应用程序。我已经设置了 KeepAlive 以确保每秒发送一条消息以检查连接是否仍然正常。 Wireshark 显示 KeepAlive 消息已发送,并且还被另一台计算机确认。我的申请是 运行 windows。
与 network/internet 的其余部分相比,我打开套接字的远程计算机位于单独的 LAN 适配器上。
有两种情况似乎反应方式不同,我想知道为什么。
- 我已连接到互联网。 TCP Keep-Alive 每秒都在变强。我从机器上拔下 LAN 电缆。最后一个 TCP Keep-Alive 不是 ACKd,因此
Socket.Connected
是false
,正如预期的那样。 - 我没有连接到互联网。 TCP Keep-Alive 每秒都在变强。我从机器上拔下 LAN 电缆。 没有 TCP Keep-Alive 包被发送到远程机器,因此
Socket.Connected
仍然是true
.
我是否遗漏了一个关于套接字何时发送 TCP Keep-Alive 包的重要概念?例如,如果您根本没有网络连接就不要发送?如果是这样,我如何确保套接字不再 Connected
?
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.SetupKeepAlive();
_socket.Blocking = false;
public static void SetupKeepAlive(this Socket socket, uint interval = 1000U)
{
if (socket is null)
{
throw new ArgumentNullException(nameof(socket));
}
// Get the size of the uint to use to back the byte array
var size = Marshal.SizeOf(0U);
// Create the byte array
var keepAlive = new byte[size * 3];
// Pack the byte array:
// Turn keep-alive on
Buffer.BlockCopy(BitConverter.GetBytes(1U), 0, keepAlive, 0, size);
// Set amount of time without activity before sending a keep-alive
Buffer.BlockCopy(BitConverter.GetBytes(interval), 0, keepAlive, size, size);
// Set keep-alive interval
Buffer.BlockCopy(BitConverter.GetBytes(interval), 0, keepAlive, size * 2, size);
// Set the keep-alive settings on the underlying Socket
_ = socket.IOControl(IOControlCode.KeepAliveValues, keepAlive, null);
}
套接字不知道 Internet 连接的可用性,但它知道网络设备状态。它只是一个非常低级的TCP单元。
它只能发送和接收东西,不能诊断网络设备后面的网络。我没有阅读 RFC,但您描述的行为是预期的。
当 TCP 连接建立时,会定期发送 Keep-alive。
therefore Socket.Connected is still true
看起来像是一个已知的 TCP 队首问题。套接字正在等待响应,直到发生超时被关闭。
想象一下设置:您处于糟糕的连接环境中,某些主机正在丢失数据包或挂起几秒钟。然后,如果 Socket 仅在未收到 keep-alive ping 时才会在超时之前关闭连接,您将获得一个关闭的连接。 Keep-alive 旨在使连接寿命更长而不是更短。
还有一个提示:
var keepAlive = new byte[12];
var values = MemoryMarshal.Cast<byte, int>(ref keepAlive);
values[0] = 1;
values[1] = interval; // make it int
values[2] = interval;
// the values were written directly to byte[] array
您似乎使用了错误的数字大小。数组大小应为 12 字节 (3x4) 而不是 24 字节 (3x8)。可能您将 C++ long
用作 C# long
。但是 C++ long
在 C# 中是 int
。 C# long
是 C++ long long
.