C Sharpとネットワーク - TCP Client

提供:MochiuWiki : SUSE, EC, PCB
2024年9月13日 (金) 18:53時点におけるWiki (トーク | 投稿記録)による版 (→‎サンプルコード)
ナビゲーションに移動 検索に移動

概要

TCP (Transmission Control Protocol) は、インターネットプロトコルスイートの中核をなす通信プロトコルの1つである。
TCPは信頼性の高い通信を提供する強力なプロトコルであり、C#の豊富なネットワーキングAPIと組み合わせることで、効率的なネットワークアプリケーションの開発が可能になる。

C#においても、TCPを利用したネットワークプログラミングは重要な位置を占めている。

TCPの主な特徴は、信頼性の高い通信を提供することである。
これは、データの送受信を確実に行い、パケットの損失や重複、順序の入れ替わりを防ぐ機能を持っているためである。

また、フロー制御や輻輳制御の仕組みも備えており、ネットワークの状況に応じて通信速度を調整する。

C#でTCP通信を実装する場合は、主にSystem.Net.Sockets名前空間を使用する。
この名前空間には、TcpClientクラスやTcpListenerクラス等が含まれており、これらを使用してクライアントとサーバの通信を簡単に実装できる。

TcpClientクラスを使用すると、サーバへの接続やデータの送信が可能になる。
TcpListenerクラスは、サーバ側でクライアントからの接続を待ち受けるために使用する。

実際の通信では、ストリームを介してデータのやり取りを行う。
NetworkStreamクラスを使用して、接続したソケットからデータを読み書きする。
これにより、テキストやバイナリデータを効率的に送受信することができる。

C#でTCP通信を実装する場合は、非同期プログラミングの手法を活用することが推奨される。
BeginConnectメソッドやBeginAcceptメソッド等を使用するこにより、アプリケーションの応答性を向上させることができる。

セキュリティ面では、SSL / TLS証明書を使用して通信を暗号化することが可能である。
C#では、SslStreamクラスを使用してこれを実現することができる。

また、接続の確立、切断、タイムアウトの処理、エラーハンドリング等を適切に実装することにより、安定したネットワークアプリケーションを開発することができる。


クライアント : 送信

以下の例では、TcpClientクラスを使用して、非同期でサーバに接続およびメッセージを送信している。
また、タイムアウトが発生した場合の再試行処理や高度なエラー処理等も追加することを推奨する。

NetworkStreamクラスのReadTimeoutWriteTimeoutプロパティを非同期処理と組み合わせることは非推奨である。
非同期操作でタイムアウトを実装する場合、Task.WhenAnyTask.Delayを組み合わせる方法が効果的である。

  • 互換性の問題
    ReadTimeoutプロパティおよびWriteTimeoutプロパティは、同期メソッド (Read, Write) に対してのみ有効である。
    非同期メソッド (ReadAsync, WriteAsync) には影響しない。

  • 非同期操作との不適合
    非同期操作は本質的にタイムアウトの概念と相容れない。
    非同期メソッドは完了するまで制御を返さないため、タイムアウトを設定しても期待通りに動作しない。


 using System;
 using System.Text;
 using System.Net.Sockets;
 using System.Threading.Tasks;
 
 class AsyncTcpClientSender
 {
    static async Task Main(string[] args)
    {
       string server = "<ホスト名またはIPアドレス>";  // サーバのアドレス
       int port = <ト番号>;                     // ポート番号
       int timeoutMilliseconds = 5000;            // タイムアウト (ミリ秒)
 
       try
       {
          using (TcpClient client = new TcpClient())
          {
             // サーバへ接続
             var connectTask = client.ConnectAsync(server, port);
 
             // サーバへの接続が確立するまで待機
             if (await Task.WhenAny(connectTask, Task.Delay(timeoutMilliseconds)) != connectTask)
             {  // タイムアウトが発生した場合
                throw new TimeoutException("エラー: サーバへの接続がタイムアウト");
             }
 
             using (NetworkStream stream = client.GetStream())
             {  // サーバへメッセージを送信
                string message = "Hello, Server";
                byte[] data = Encoding.UTF8.GetBytes(message);
 
                var sendTask = stream.WriteAsync(data, 0, data.Length);
                if (await Task.WhenAny(sendTask, Task.Delay(timeoutMilliseconds)) != sendTask)
                {
                   throw new TimeoutException("エラー: メッセージの送信がタイムアウト");
                }
 
                Console.WriteLine($"送信メッセージ: {message}");
             }
          }
       }
       catch (SocketException e)
       {
          Console.WriteLine($"SocketException: {e.Message}");
       }
       catch (TimeoutException e)
       {
          Console.WriteLine($"TimeoutException: {e.Message}");
       }
       catch (Exception e)
       {
          Console.WriteLine($"予期せぬエラーが発生: {e.Message}");
       }
    }
 }