I have an application that opens a read-only TCP stream socket. I have set up KeepAlive to ensure that every second a message is sent to check if the connection is still ok. Wireshark shows that the KeepAlive messages are sent and also ACKd by the other computer. My application is running on windows.
The remote machine to where I open the socket is on a separate LAN adapter compared to the rest of the network/internet.
There are two scenarios that seem to react in a different way, and I would like to know why.
- I am connected to the internet. TCP Keep-Alive is going strong every second. I pull the LAN cable from the machine. The last TCP Keep-Alive is not ACKd and therefore
Socket.Connected
isfalse
, as expected. - I am not connected to the internet. TCP Keep-Alive is going strong every second. I pull the LAN cable from the machine. No TCP Keep-Alive package was sent to the remote machine at all, therefore
Socket.Connected
is stilltrue
.
Am I missing an important concept here on when a TCP Keep-Alive package is sent by the socket? For example, don't send if you don't have a network connection at all? And if so, how can I ensure that the socket is no longer 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);
}
CodePudding user response:
Socket is not aware of Internet Connetion availability but it's aware of Network Device state. It's just a very low-level TCP unit.
It can only send and receive something not diagnosing the network behind the network device. I didn't read the RFC but the behavior you're described is expected.
Keep-alive sill be sent periodically while TCP connetion is up.
therefore Socket.Connected is still true
Looks like a known TCP head-of-line problem. Socket is waiting for response till timeout occured to be closed.
Imagine the setup: you're in poor connection environment, some host is loosing packets or hang for seconds. Then if Socket will close the connection before timout only when not received the keep-alive ping, you'll get a closed connection. Keep-alive is intended to make the connection lifetime longer not shorter.
Also a tip:
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
Looks like you used the wrong number sizes. Array size expected 12 bytes (3x4) not 24 bytes (3x8). Probably you used C long
as C# long
. But C long
is int
in C#. C# long
is C long long
.