6章:TCPサーバプログラム自作の検討(2)

    作成2015.10.11
     TCPサーバプログラムを自作した経験がなかったため、まずはサンプルプログラムを検討してみました。

  1. TCPサーバサンプルプログラム
     以下のサンプルプログラムをテストしてみました。
    (1)同期サーバーのソケットの例
    https://msdn.microsoft.com/ja-jp/library/6y0e13d3(v=vs.110).aspx

    (2)非同期なサーバーのソケットの例
    https://msdn.microsoft.com/ja-jp/library/fx6588te(v=vs.110).aspx

    (3)TcpListener クラス
    https://msdn.microsoft.com/ja-jp/library/system.net.sockets.tcplistener(v=vs.110).aspx

    (4)C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)
    http://note.chiebukuro.yahoo.co.jp/detail/n1657


  2. 動作テスト環境
    (1)TCPサーバプログラム作成用
    Visual Studio Express 2012 for Windows Desktop

    (2)COMポート用プログラム
    まずは下記のCsharp Simple CDC Demo.exe:実行ファイルを使用します。
    [2-1.zip]をダウンロードする。

    解凍するとフォルダー内にMicrosoft Visual C#2012フォルダーがあります。フォルダー内に
    (1)*Basic Communicationフォルダー:ソースファイル群
    (2)Csharp Simple CDC Demo.exe:実行ファイル
    があります。

    (3)シリアル_USB変換プログラム
     シリアル-USB変換モジュールソースプログラム(送信側)

    と同一となります。

    [../f41/26-1.zip]をダウンロードする。

    解凍するとMy-CDC-Basic.Xフォルダーがあります。

    ボーレートの変更:UART_Init(9600);→UART_Init(115200);に変更します。


    (4)ブレークアウトボードのピン設定
     (1)ENピン:(Chip Enable.)→Highに設定します。
     (2)GPIO-15ピン:(Type I/O MTDO;HSPI_CS; UART0_RTS)→Lowに設定します。
     (3)GPIO-2ピン:(Type I/O UART Tx during flash programming)→Highに設定します。
     (4)GPIO-0ピン:(Type I/O SPI_CS2)→Highに設定します。
     (5)TXピン:(Type I/O GPIO-1)→通信相手のRXに接続
     (6)RXピン:(Type I/O GPIO-3)→通信相手のTXに接続
     (7)3V3ピン:→3.3V電源供給
     (8)GNDピン:→アース


  3. 評価回路外観
     評価回路外観を以下に示します。





  4. TcpListener クラスのテスト
    https://msdn.microsoft.com/ja-jp/library/system.net.sockets.tcplistener(v=vs.110).aspx

    (1)Visual Studio Express 2012 for Windows Desktopを管理者として実行します。
    (2)メニューの「ファイル」_「新しいプロジェクト」を選択します。
    (3)「テンプレート」_「Visual C#」_「コンソールアプリケーション」を選択します。
    (4)デフォルトの名前「ConsoleApplication1」のまま「OK」ボタンを押します。
    (5)Program.cs の内容を消去し、サンプルプログラムをペーストします。
    (6)IPAddress localAddr = IPAddress.Parse("127.0.0.1");のアドレスをパソコンのIPアドレスに合わせて変更します。
    (7)メニューの「デバッグ」_「デバッグ開始」を選択します。
    (8)コンソールに以下が表示されます。
    Waiting for a connection…
    (9)メニューの「デバッグ」_「全て中断」を選択します。
    (10)TcpClient client = server.AcceptTcpClient();で停止していることが確認できます。
    (11)AE-USBPIC44基板のUSBを接続します。
    (12)Csharp Simple CDC Demo.exeを起動します。
    (13)COMポートを選択して、「Connect」ボタンを押します。
    (14)ESP-WROOM-02に電源を供給しします。
    (15)ボーレート76800bpsで以下データを受信します。
    ets Jan 8 2013,rst cause:1, boot mode:(3,0)

    load 0x40100000, len 1396, room 16
    tail 4
    chksum 0x89
    load 0x3ffe8000, len 776, room 4
    tail 4
    chksum 0xe8
    load 0x3ffe8308, len 540, room 4
    tail 8
    chksum 0xc0
    csum 0xc0

    2nd boot version : 1.4(b1)
    SPI Speed : 40MHz
    SPI Mode : QIO
    SPI Flash Size & Map: 8Mbit(522KB+512KB)
    jump torrun user1 @ 1000
    (16)ボーレート115200bpsで以下データを受信します。
    ready
    WIFI CONNECTED
    WIFI GOTIIP
    (17)送信文字列に「AT+CWMODE=1」を入力して、「\r\n有り送信」ボタンを押します。
    (18)送信文字列に「AT+CIPSTART="TCP","192.168.11.2",13000」を入力して、「\r\n有り送信」ボタンを押します。
    (19)TCPプログラムは接続を完了して、int bytesRec = handler.Receive(bytes);まで進行します。
    (20)送信文字列に「AT+CIPMODE=1」を入力して、「\r\n有り送信」ボタンを押します。
    (21)送信文字列に「AT+CIPSEND」を入力して、「\r\n有り送信」ボタンを押します。
    (22)以上でトランスペアレントモードの初期設定が完了してATコマンドは使用できなくなります。
    (23)送信文字列に「AT+CIPSEND」を入力して、「\r\n有り送信」ボタンを2回押します。
    (24)以下のような画面となります。





  5. TcpListener クラスの分析
    (1)TcpClient client = server.AcceptTcpClient();を実行すると、TcpClientの接続待ちとなる。接続待ちの間は、実行が完了しないため、サーバー アプリケーションの実行が中止されロック状態となる。
    (2)stream.Read(bytes, 0, bytes.Length)を実行すると、NetworkStreamのデータ受信待ちとなる。データ受信待ちの間は、実行が完了しないため、サーバー アプリケーションの実行が中止されロック状態となる。
    (3)本サンプルでは、データ受信のタイミングでデータ返信を行っている。
    (4)本サンプルでは、データ受信のタイミングでデータ返信を行った後、接続を切断している。
    (5)接続の切断を行わない場合は、任意のタイミングでサーバ側からクライアント側に送信が可能である。
    (6)以下のコードに変更するとデータ受信待ちのロック状態を防止できる。
    if (stream.DataAvailable)
     {
          int i = stream.Read(bytes, 0, bytes.Length);
          if (i != 0)
           {
                data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                textBox3.AppendText(data);
          }
    }
    

    長所:
    (1)コード記述が最も簡潔でわかりやすい。
    (2)データ受信後の接続の切断をやめることにより、任意のタイミングでサーバ側からクライアント側に送信が可能である。
    短所:
    (1)TcpClientの接続待ちの状態で接続が完了するまでの間、サーバー アプリケーションがロック状態となる。
    (2)データ受信待ちも同様にロック状態となるが、stream.DataAvailable を使用することによりロック状態を防止できる。 R2
    (3)ロック状態から終了する場合は、Windowsタスクマネージャーを使用して強制終了が必要となる。


  6. C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)のテスト
    http://note.chiebukuro.yahoo.co.jp/detail/n1657

    (1)http://note.chiebukuro.yahoo.co.jp/detail/n1657に詳細の手順が記載されており、そのとおり実行します。
    (2)server = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 9000);のアドレスをパソコンのIPアドレスに合わせて変更します。
    (3)string str = System.Text.Encoding.Unicode.GetString(ReceiveData, 0, DataLength);をSystem.Text.Encoding.ASCII.GetStringに変更します。
    (4) byte[] SendBuffer = System.Text.Encoding.Unicode.GetBytes("本サーバーの御利用ありがとう御座います。");を byte[] SendBuffer = System.Text.Encoding.ASCIIGetBytes("TCP-TEST\r\n");に変更します。
    (5)メニューの「デバッグ」_「デバッグ開始」を選択します。
    (6)「サーバ開始」ボタンを押します。
    (7)AE-USBPIC44基板のUSBを接続します。
    (8)Csharp Simple CDC Demo.exeを起動します。
    (9)COMポートを選択して、「Connect」ボタンを押します。
    (10)ESP-WROOM-02に電源を供給しします。
    (11)送信文字列に「AT+CWMODE=1」を入力して、「\r\n有り送信」ボタンを押します。
    (12)COM6側の送信文字列に「AT+CWJAP="SSID","password"」を入力し、「\r\n有り送信」ボタンを押します。
    (13)送信文字列に「AT+CIPSTART="TCP","192.168.11.2",9000」を入力して、「\r\n有り送信」ボタンを押します。
    (14)TCPプログラムは接続を完了して、int bytesRec = handler.Receive(bytes);まで進行します。
    (15)送信文字列に「AT+CIPMODE=1」を入力して、「\r\n有り送信」ボタンを押します。
    (16)送信文字列に「AT+CWJAP="SSID","password"」を入力し、「\r\n有り送信」ボタンを押します。
    (17)送信文字列に「AT+CIPSEND」を入力して、「\r\n有り送信」ボタンを押します。
    (18)以上でトランスペアレントモードの初期設定が完了してATコマンドは使用できなくなります。
    (19)送信文字列に「AT+CIPSEND」を入力して、「\r\n有り送信」ボタンを2回押します。
    (20)以下のような画面となります。





  7. C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)の分析
    (1)このサンプルプログラムは重大な問題点が確認されました。
    (2)プログラムを終了するとメインスレッドのプログラムは終了するが、ListeningCallbackThreadが終了せずに動作中となります。
    (3)メインスレッドが終了するため、一見プログラム終了のように見えますが、ListeningCallbackThreadが動作しているため実行ファイルの消去ができなくなります。
    (4)Windowsタスクマネージャーで終了プログラムは認識されないため、ListeningCallbackThreadの強制終了ができません。
    (5)ListeningCallbackThreadの強制終了の手段はパソコンの再起動となります。
    (6)TcpListener クラスのserver.AcceptTcpClient(); は条件が満足するまで無限ループとなります。
    (7)同様に stream.Read(ReceiveData, 0, ReceiveData.Length);は条件が満足するまで無限ループとなります。
    (8)このサンプルプログラムの特徴は、無限ループとなる可能性のある関数の実行をメインスレッドではなく、ListeningCallbackThreadスレッドで実行している点です。
    (9)メインスレッドは無限ループがないため、操作がロックすることはありませんが、ListeningCallbackThreadスレッドがロックする可能性があります。
    (10)データ受信時のロックは、回避する方法がありますが、接続待ちのロックは回避する方法がみいだせません。
    (11)本サンプルプログラムは、データ受信時のロックを回避していないため、高い確率でListeningCallbackThreadがロック状態となる可能性があります。


  8. TCPサーバプログラム自作の検討(2)まとめ
    (1)TCPサーバサンプルプログラムは多数みつかったのですが、その中から4種を抜粋して検討してみました。
    (2)同期サーバーのソケットの例はコードの記述は簡潔でわかりやすい特徴があります。
    (3)反面、接続待ちの状態で接続されないとサーバー アプリケーションがロック状態となる問題があるます。
    (4)しかし、データ受信待ちのロック状態は防止可能です。
    (5)非同期なサーバーのソケットの例は接続待ちもデータ受信待ちもロック状態となりません。
    (6)反面、プログラムコードは複雑で難解です。
    (7)同期サーバーのソケットも非同期なサーバーのソケットもデータ受信後の接続の切断をやめることにより、任意のタイミングでサーバ側からクライアント側に送信が可能である。
    (8)TcpListener クラスは同期サーバーのソケットの例よりもさらに簡潔な記載となる。
    (9)TcpListener クラスは同期サーバーのソケットの例と同様に接続待ちとデータ受信待ちでロック状態となる。
    (10)C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)はロック状態となる関数の実行をListeningCallbackThreadスレッドで実行するため、メインスレッドはロック状態とならない。しかし、ListeningCallbackThreadスレッドが終了しない問題が発生する。


  9. 第1次の自作プログラムの方式選択
    (1)本格、実用プログラムは非同期なサーバーのソケットの例の適用が必要となるかもしれません。
    (2)しかし、第1次の自作プログラムは記述が最も簡潔なTcpListener クラスを採用したいと思います。
    (3)TcpListener クラスを使用した場合、接続待ちのロックを防止する有効な手段をみいだせません。
    (4)データ読取待ちのロックは、防止可能です。
    (5)接続待ちの命令をメインスレッド以外で実行すると難解なトラブルが発生する可能性があります。
    (6)接続待ちの命令をメインスレッドで実行すると、接続待ちの間はロック状態となります。
    (7)ロック状態でアプリケーションを終了するには、Windowsタスクマネージャーを使用して強制終了する必要があります。
    (8)第1次の自作プログラムの方式はTcpListener クラスを使用し、メインスレッドのみで作成したいと思います。




7章:TCPサーバプログラム自作の検討(3)に行く。

トップページに戻る。