いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

dotnet (.net framework) でキープアライブプローブを送信する (Windows, Linux, Keep Alive Probe)

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

.net で キープアライブプローブを送信するやり方を調べる必要があり、調査ついでに以下のリポジトリを作っておきました。

github.com

以下、その内容を抜粋。

サンプル

サーバ側

とりあえず、接続を受け付けるものが必要なので、以下のようにしてListenするようにしておきます。

using System;
using System.Net;
using System.Net.Sockets;

var port = 12345;
var addr = IPAddress.Parse("127.0.0.1");
var server = new TcpListener(addr, port);

server.Start();

for (; ; )
{
    using (var client = server.AcceptTcpClient())
    {
        var buf = new byte[10];
        using (var s = client.GetStream())
        {
            var n = s.Read(buf, 0, buf.Length);
            Console.WriteLine("[server] read {0}", n);
        }

        client.Close();
    }

    break;
}

受け付けたら何もせずにジーッとしておく

クライアント

こちらが今回の対象。ソケットを生成したらキープアライブの設定を行います。

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

var port = 12345;
var addr = IPAddress.Parse("127.0.0.1");

using (var client = new TcpClient())
{
    client.Connect(addr, port);

/*
    // For Windows
    {
        const uint on = 1;
        const uint time = 2000;
        const uint interval = 2000;

        byte[] inOptionValues = new byte[Marshal.SizeOf(on) * 3];

        BitConverter.GetBytes(on).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes(time).CopyTo(inOptionValues, Marshal.SizeOf(on));
        BitConverter.GetBytes(interval).CopyTo(inOptionValues, Marshal.SizeOf(on) * 2);

        client.Client.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }
*/

    // For Linux
    {
        const bool on = true;
        const int time = 2;
        const int interval = 2;

        var sock = client.Client;
        sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, on);
        sock.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, time);
        sock.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, interval);
    }

    using (var s = client.GetStream())
    {
        s.ReadTimeout = (int)TimeSpan.FromSeconds(10).TotalMilliseconds;

        try
        {
            var buf = new byte[10];
            var n = s.Read(buf, 0, buf.Length);
        }
        catch (IOException)
        {
        }
    }

    client.Close();
}

Linuxの場合は、SetSocketOption でCのように設定できますが、Windowsの場合はちょっとクセがありますね。

実行すると、以下のようなキャプチャが見れます。ちゃんとキープアライブプローブが送信されていますね。

実行方法については、上記のリポジトリを参照ください。

$ task capture
task: [capture] sudo tcpdump -i lo 'tcp and port 12345 and tcp[13] & 16 != 0 and tcp[12] & 15 = 0'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
07:16:46.205364 IP localhost.12345 > localhost.51758: Flags [S.], seq 2513543126, ack 2626466582, win 43690, options [mss 65495,sackOK,TS val 4204759295 ecr 4204759295,nop,wscale 7], length 0
07:16:46.205387 IP localhost.51758 > localhost.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204759295 ecr 4204759295], length 0
07:16:48.210721 IP localhost.51758 > localhost.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204761300 ecr 4204759295], length 0
07:16:48.210733 IP localhost.12345 > localhost.51758: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204761300 ecr 4204759295], length 0
07:16:50.222709 IP localhost.51758 > localhost.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204763312 ecr 4204761300], length 0
07:16:50.222716 IP localhost.12345 > localhost.51758: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204763312 ecr 4204759295], length 0
07:16:52.238732 IP localhost.51758 > localhost.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204765328 ecr 4204763312], length 0
07:16:52.238739 IP localhost.12345 > localhost.51758: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204765328 ecr 4204759295], length 0
07:16:54.254711 IP localhost.51758 > localhost.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204767344 ecr 4204765328], length 0
07:16:54.254717 IP localhost.12345 > localhost.51758: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204767344 ecr 4204759295], length 0
07:16:56.274647 IP localhost.51758 > localhost.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204769364 ecr 4204767344], length 0
07:16:56.274658 IP localhost.12345 > localhost.51758: Flags [.], ack 1, win 342, options [nop,nop,TS val 4204769364 ecr 4204759295], length 0
07:16:56.410255 IP localhost.51758 > localhost.12345: Flags [F.], seq 1, ack 1, win 342, options [nop,nop,TS val 4204769500 ecr 4204769364], length 0
07:16:56.414705 IP localhost.12345 > localhost.51758: Flags [.], ack 2, win 342, options [nop,nop,TS val 4204769504 ecr 4204769500], length 0
07:16:56.425536 IP localhost.12345 > localhost.51758: Flags [F.], seq 1, ack 2, win 342, options [nop,nop,TS val 4204769515 ecr 4204769500], length 0
07:16:56.425547 IP localhost.51758 > localhost.12345: Flags [.], ack 2, win 342, options [nop,nop,TS val 4204769515 ecr 4204769515], length 0

Linuxで実行すると、送信されるキープアライブプローブはペイロードが0バイトで送信されます。

Windowsで実行すると、送信されるキープアライブプローブはペイロードが1バイトで0x00固定で送信されます。

参考情報


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。