Windows 10(未だに)でVisual Studio 2010(未だに)で C#でTcpClientでKeepAliveをしたい。
とりあえず生TcpClient(SSLではないという意味)でやってみる。

サーバー側はこんな感じ。WinForm(未だに)でフォームを一つ作ってボタンを2個配置して待ち受けるボタンとゼロバイトの送信を行う(これではKeepAliveはできなかった)。

        private TcpListener listener = null;
        private TcpClient client = null;

        private void button1_Click(object sender, EventArgs e)
        {
            if (client != null)
            {
                client.Close();
                client = null;
            }
            if (listener == null)
            {
                listener = new TcpListener(IPAddress.Any, 9999);
                listener.Start();
            }
            client = listener.AcceptTcpClient(); // これだとクライアントが接続してくるまで固まるのでスレッドとかうまく使ってくれ。
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (client != null)
            {
                byte[] bs = new byte[0];
                client.GetStream().Write(bs, 0, bs.Length); // これではダメ。パケットが送信されない。
            }
        }

クライアント側も同じようにフォームを作ってボタンを2個。接続ボタンと通信ボタン。ネットから拾ってきたKeepAlive有効のコードを追加。したのだが・・・・。

        private TcpClient client = null;

        private void button1_Click(object sender, EventArgs e)
        {
            if (client != null)
            {
                client.Close();
                client = null;
            }
            client = new TcpClient("127.0.0.1", 9999);
            client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); // これはこれでKeepAliveを有効にするがWindowsのデフォルト設定になる。下記のIOControlをするなら不要。
            byte[] tcp_keepalive = new byte[12];
            //onoffスイッチ.
            BitConverter.GetBytes((UInt32)1).CopyTo(tcp_keepalive, 0); // 0がオフ、(1ではなく)0以外がオンのようだ。
            //wait time.(ms)
            BitConverter.GetBytes((UInt32)5000).CopyTo(tcp_keepalive, 4); // KeepAliveパケットはこちらの秒数ごとに送られている。
            //interval.(ms)
            BitConverter.GetBytes((UInt32)28000).CopyTo(tcp_keepalive, 8); // これが何に使われているのか分からん。ネット上の説明ではこれで28秒ごとにKeepAliveのはずだが。
            // keep-aliveのパラメータ設定
            client.Client.IOControl(IOControlCode.KeepAliveValues, tcp_keepalive, null);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (client != null)
            {
                byte[] bs = new byte[3];
                bs[0] = (byte)'a';
                bs[1] = (byte)'b';
                bs[2] = (byte)'c';
                client.GetStream().Write(bs, 0, bs.Length);
            }
        }

このようなコードでKeepAliveパケットが送信される事はWireSharkで確認できたのだが、tcp_keepalive構造体の2つめはTCP開始後に待つミリ秒数で3つめが待った後のKeepAlive送信間隔のはずなのに、実際はTCP接続直後から通信無しの状態とクライアント側のボタン2押下で”abc”を送った後の状態で上記の5000ミリ秒後にKeepAliveが送信されその後も5000ミリ秒ごとに送られている。インターバルとされている28000ミリ秒はどこにも出てこない。5000と28000を逆に(wait timeの方を小さく)してもやはりwait timeに当たる値しか使われてないように見える。

KeepAliveパケットが意図した間隔で送受信できれば良いので実プログラムに組み込むのは問題が無いのだが、なんだかよく分からない動きだ。