1
votes

We are looking for a way to enable TCP keepalive for client sockets on Windows CE 6.0, using .net Compact framework 3.5.

So far I've found these options:

Set keepalive using SetSocketOption on the System.Net.Socket class:

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

This actually work, but uses the windows-global settings for keepalive, and that is to check the connection every second hour, which is way too seldom for our use case. This timeout setting can be changed (globally) by altering registry keys under [HKEY_LOCAL_MACHINE\Comm\Tcpip\Parms]. This will be my fallback solution, but I'd rather set the timeouts on a per-connection basis.

Then I have tried using socket.IoControl as suggested by several sources online to set this socket option including the timeout values, but it only results in an SocketException (“An invalid argument was supplied”).

Example

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

// The native structure for this is defined in mstcpip.h as:
//struct tcp_keepalive {
//u_long onoff;
//u_long keepalivetime;
//u_long keepaliveinterval;
//};

//Set to on, 10 seconds and 1 second. From examples online u_long should be interpreted as unsigned 32bit.
byte[] inValue = new[] { (uint)1, (uint)10000, (uint)1000 }
.SelectMany(x => BitConverter.GetBytes(x))
.ToArray();

//0x98000004 is constant for SIO_KEEPALIVE_VALS found in mstcpip.h
int ioControlCodeKeepAliveValues = BitConverter.ToInt32(BitConverter.GetBytes(0x98000004), 0);

// Throws "An invalid argument was supplied" SocketException 
socket.IOControl(ioControlCodeKeepAliveValues, inValue, null);

socket.Connect(new IPEndPoint(IPAddress.Parse("192.168.0.120"),80));

Is this code correct, or is it a known limitation of Windows CE 6 that keep-alive can not be set per connection?

4

4 Answers

1
votes

It's been a while, but I looked at this as well. I decided in the end that fiddling with the registry would be non-portable and easy to forget when installing on a new machine. I can't quite remember what things I tried, but I do recall it was quite tedious and ended up not producing a satisfying solution. I think there were a number of occasions where some API call in the docs looked like the right thing, but turned out to be a waste of time. I should note I was on Windows Server, not CE.

In the end, I implemented keep-alive manually, ie by sending something through the connection. This is pretty easy to do, since you can just have a low priority thread doing this occasionally.

0
votes

Also, you have to remember that TCP build-in Keep-Alive mechanizm is also not very reliable. Going through many papers (for example from Cisco), I noticed that you don't have any warranty that network devices will transport TCP packets with 0 length, which basically is a Keep-Alive packet. Even TCP RFC urged not to use this solution:

Implementors MAY include "keep-alives" in their TCP implementations, although this practice is not universally accepted. If keep-alives are included, the application MUST be able to turn them on or off for each TCP connection, and they MUST default to off.

So long story short - when Keep-Alive is received, you know connection is up, but when it doesn't - you know nothing. Pretty much like John Snow.

In my opinion best solution has already been posted - you can add another thread on each ends which only listens for your own Keep-Alive (or any) packets and inform you when something goes wrong.

0
votes

I don't have access to a CE6.0 device at the moment, but the code as shown

  • works on CE7.0 (IOControl call succeeds)
  • does not work on CE5.0 (throws SocketException)

My guess therefore would be that SIO_KEEPALIVE_VALS is simply not supported on CE6.0.

As an aside, rather than calling BitConverter twice, the ioctl code can be produced like this:

int ioctl;
unchecked { ioctl = (int)0x98000004; }

Personally I find this more readable, but then again not everyone likes unchecked blocks and casts in their code.

0
votes

I've just tried to use the SIO_KEEPALIVE_VALS code for the WSAIoctl() function from my Windows CE 7 application. Although WSAIoctl(SIO_KEEPALIVE_VALS) returns 0, which means success, but TCP Keepalive packets are not being sent (tested in Wireshark). So the code seems to not work on Windows CE.

So seems like setting Keep-alive parameters (time, period and count) system-wide in registry and then enabling Keepalive for a socket in application code is the only solution on Windows CE.

Or just use some another, more reliable mechanism of detecting broken connections, like heart-beat messages.