C#でSNMPメッセージのデコーディングの実装のためのSNMP受信メッセージの見える化を行う

#概要

前回の[「C#でSNMPのGetNextメッセージをエンコードする」](https://qiita.com/sukkyxp/items/45d508446810aa217265)と
前々回の[「C#でASN.1のObject Identifierのエンコードを行う」](https://qiita.com/sukkyxp/items/69e142d07aa92aaa09c2)迄で
SNMPのGetNextRequestメッセージのエンコードを行うクラスを実装しました。
次は受信したSNMPのメッセージをデコードする処理の実装になりますが、その前に実際にC#のUdpClientを使ってSNMPメッセージを送信、受信して受信したメッセージを16進表記文字列（ヘキサストリング）で表示するテストプログラムを作成します。

#テストフォームの作成

下記の通り、送信先のアドレスと取得するOIDを入力して「送信」ボタン押下により、
前回作成した「SnmpGetNextMessage」クラスでSNMPのメッセージのエンコードを実施し、
送信メッセージにエンコードされたバイト配列を16進文字列で「送信Message」に表示した後に、
SNMPの送信を行い、受信したSNMPのメッセージのバイト列を16進表記文字列で「受信Message」に表示する
画面を準備する。

![SNMPTestForm.png](https://qiita-image-store.s3.amazonaws.com/0/197947/27e0ddce-1b11-4c8a-65b1-cd10709d9fa2.png)

#UdpClientのインスタンス作成と待受

フォームクラスのインスタンス変数として、udpClient変数を宣言する。

```C#
private UdpClient udpClient;
```

フォームのLoadイベントでインスタンスの生成を行う。

```C#
        private void Form1_Load(object sender, EventArgs e)
        {
            udpClient = new UdpClient(0);
            udpClient.BeginReceive(ReceiveCallback, udpClient);

        }
```

なお、クラスのヘッダ部に下記のusingが必要です。

```C#
using System.Net;
using System.Net.Sockets;
```

UdpClientコンストラクタでポート番号を引数で渡すことにより、UDPのListenを開始するようにします。
ポート番号に「0」を指定することにより任意の空きポートが自動的に使用されます。

BeginReceiveで非同期で待受処理を開始します。
UDPメッセージを受信するとReceiveCallbackが呼び出されます。

ReceiveCallbackでは一旦受信を中断して受信したデータを受け取ったあとに、BeginReceiveにより受信(Listen)を再開しています。
また受信したメッセージを16進表記の文字列に変換してForm上のテキストボックスに表示しています。

```C#
        private void ReceiveCallback(IAsyncResult ar)
        {
            UdpClient udp = (UdpClient)ar.AsyncState;

            IPEndPoint remoteHost = null;
            byte[] receiveData;
            try
            {
                receiveData = udp.EndReceive(ar, ref remoteHost);
            }
            catch (SocketException ex)
            {
                ShowReceiveMessage(ex.Message);
                return;
            }
            catch (ObjectDisposedException ex)
            {
                ShowReceiveMessage(ex.Message);
                return;
            }

            ShowReceiveMessage(BitConverter.ToString(receiveData).Replace("-", " "));

            // 非同期受信開始
            udp.BeginReceive(ReceiveCallback, udp);
        }

```

ShowReceiveMessageメソッドではちょっとした仕掛けが必要になります。
非同期で呼び出された「ReceiveCallback」の中からフォームのコントロールに直接アクセスすることは出来ないためです。
InvokeRequiredにより、コントロールに直接アクセスできるスレッド上からの起動かどうかを確認しています。
InvokeRequiredがtrueの場合は、Invokeにより再度自分自身を呼び直しています。
こうすることにより、非同期処理からフォーム上のコントロールにアクセスが可能になります。

```C#

        delegate void ShowReceiveMessageDelegate(String message);

        void ShowReceiveMessage(String message)
        {
            if (InvokeRequired)
            {
                // 別スレッドから呼び出された場合
                Invoke(new ShowReceiveMessageDelegate(ShowReceiveMessage), message);
                return;
            }

            txtReceive.Text = message;

        }
```

#メッセージ送信処理

送信ボタンのイベントでSnmpGetNextMessageクラスを使ってメッセージの生成と、
udpClientによるSNMPメッセージの送信処理を行います。
SnmpGetNextMessageを使ってメッセージのエンコードを実施し、エンコードのバイト配列を
16進表記文字列でテキストボックスに出力しています。

```C#
        private void btnSendSnmpGetNextRequestMessage_Click(object sender, EventArgs e)
        {
            SnmpGetNextMessage getNextMessage = new SnmpGetNextMessage("public");
            getNextMessage.AddRequestOid(txtOriginal.Text);

            string remoteHost = txtIp.Text;
            int remotePort = 161;

            byte[] sendData = getNextMessage.GetBytes().ToArray();

            txtSendMessage.Text = BitConverter.ToString(sendData).Replace("-"," ");

            udpClient.Send(sendData, sendData.Length, remoteHost, remotePort);

        }
```

#UdpClientの後始末

今回はFormのFormClosingイベントで後始末を実施します。

```C#
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (udpClient != null)
            {
                try
                {
                    udpClient.Close();
                } catch(Exception){
                    // NOP
                }
            }
        }
```

#試してみます

Windows10 Pro上で試しています。

![SnmpTestFormReceive.png](https://qiita-image-store.s3.amazonaws.com/0/197947/5ea10d09-1923-bc5d-6213-87540a8e2c44.png)

なお、Windows10 ProをSNMPエージェントにする方法は前々回の[「C#でASN.1のObject Identifierのエンコードを行う」](https://qiita.com/sukkyxp/items/69e142d07aa92aaa09c2)で紹介していますので、そちらを参照お願いします。

#最後に
次回は受信したSNMPメッセージのデコード処理を行うクラスを実装する予定です。
なお、ここで使用しているUDPの送受信処理はSNMPメッセージのデコード処理を実装するために、デコード対象となるSNMPメッセージを取得するための実装です。
また、送受信メッセージを16進表記文字列で画面上に表示することにより、「見える化」しています。
「見える化」「さわれる化」または「いじれる化」はネットワークプログラミングをする上で、楽しくプログラムするための秘訣だと思います。