Keepalive su socket C#

Un cliente mi ha chiesto di attivare il keepAlive su un socket in un applicativo c#, così su due piedi ho detto: Facile ! Basta usare:

   1:  private TcpListener listener;
   2:  listener.Server.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1);//per abilitare il tcpkeepalive sul socket

Però il settaggio sopra serve solo per attivare il keepAlive su un socket utilizzando i valori di default da settare nel registry:

[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters]

KeepAliveTime = millisecondi
Specifica l’idle time in millisecondi prima che il TCP cominci ad inviare keepalive. Default 2 ore (7.200.000)

KeepAliveInterval = uint
Specifica l’intervallo in secondi tra le ritrasmissioni dei keepalive dopo che il KeepAliveTime è scaduto. Default 1 secondo (1)

Purtroppo il requisito del cliente era quello di settare differenti intervalli di KeepAlive e quindi ho dovuto prendere un approccio leggermente più complesso che ho trovato dopo una bella sessione di Google:

   1:  private TcpListener listener;
   2:  SetTcpKeepAlive(listener.Server, 5000, 1);
   3:  listener.Server.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1);// per abilitare il keepalive
   4:   
   5:  public static void SetTcpKeepAlive(Socket socket, uint keepaliveTime, uint keepaliveInterval)
   6:          {
   7:              /* the native structure
   8:              struct tcp_keepalive {
   9:              ULONG onoff;
  10:              ULONG keepalivetime;
  11:              ULONG keepaliveinterval;
  12:              };
  13:              */
  14:   
  15:              // marshal the equivalent of the native structure into a byte array
  16:              uint dummy = 0;
  17:              byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
  18:              BitConverter.GetBytes((uint)(keepaliveTime)).CopyTo(inOptionValues, 0);
  19:              BitConverter.GetBytes((uint)keepaliveTime).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
  20:              BitConverter.GetBytes((uint)keepaliveInterval).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
  21:   
  22:              // write SIO_VALS to Socket IOControl
  23:              socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
  24:          }

 

In questo modo è possibile settare i valori di KeepAlive su singolo socket.