TCP Keepalive(以下简称Keepalive)是一种用于检测TCP连接是否仍然活跃的机制。在网络编程中,这种机制可以帮助确定一个连接是否仍然有效,或者是否已经因为某些原因(如对方重启)而断开。
在大多数情况下,了解TCPKeepalive并不是必要的,但在某些特定情况下,它却非常有用。要理解Keepalive,需要具备一些基本的TCP/IP网络概念。
Keepalive的概念非常简单:当建立一个TCP连接时,会关联一组计时器。其中一些计时器用于Keepalive过程。当Keepalive计时器达到零时,会向对等方发送一个Keepalive探测包,该包不包含任何数据,但ACK标志被打开。可以这样做,因为TCP/IP规范允许发送一个类似于重复ACK的探测包,而远程端点不会有任何异议,因为TCP是一个面向流的协议。另一方面,将收到来自远程主机的回复(该主机不需要支持Keepalive,只需要支持TCP/IP),回复中没有数据,ACK被设置。
如果收到了Keepalive探测包的回复,可以断言连接仍然活跃,而不必担心用户级别的实现。实际上,TCP允许处理流,而不是数据包,因此一个零长度的数据包对用户程序来说并不危险。
这个程序非常有用,因为如果其他对等方失去了连接(例如通过重启),将注意到连接已经断开,即使没有流量。如果Keepalive探测没有得到对等方的回复,可以断言该连接不能被认为是有效的,然后采取正确的行动。
SetKeepAliveValues方法可以启用或禁用每个连接的TCP Keepalive选项设置,该选项指定用于TCP Keepalive包的TCP Keepalive超时和间隔。有关Keepalive选项的更多信息,请参见RFC 1122的第4.2.3.6节,该节在IETF网站上有指定。
传递给Socket.IOControl的optionInValue参数应该指向一个tcp_keepalive结构,该结构在Mstcpip.h头文件中定义。这个结构定义如下:
struct tcp_keepalive {
u_long onoff;
u_long keepalivetime;
u_long keepaliveinterval;
};
将tcp_keepalive C结构转换为C#结构:
[StructLayout(LayoutKind.Explicit)]
unsafe struct TcpKeepAlive {
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public fixed byte Bytes[12];
[FieldOffset(0)]
public uint On_Off;
[FieldOffset(4)]
public uint KeepaLiveTime;
[FieldOffset(8)]
public uint KeepaLiveInterval;
}
以下是如何调用该方法的示例:
public int SetKeepAliveValues(System.Net.Sockets.Socket socket, bool onOff, uint keepAliveTime, uint keepAliveInterval) {
int result = -1;
unsafe {
TcpKeepAlive keepAliveValues = new TcpKeepAlive();
keepAliveValues.On_Off = Convert.ToUInt32(onOff);
keepAliveValues.KeepaLiveTime = keepAliveTime;
keepAliveValues.KeepaLiveInterval = keepAliveInterval;
byte[] inValue = new byte[12];
for (int i = 0; i < 12; i++)
inValue[i] = keepAliveValues.Bytes[i];
result = socket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
}
return result;
}
System.Net.Sockets.Socket socket = new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp
);
// 设置10小时: 10 * 60 * 60 * 1000 = 36,000,000 每1秒1000
SetKeepAliveValues(socket, true, 36000000, 1000);