「C Sharpとネットワーク - TCP Client」の版間の差分

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

編集の要約なし
 
(同じ利用者による、間の4版が非表示)
104行目: 104行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
==== 送信 (SSL / TLS対応) ====
以下の例では、SSL / TLS証明書を使用して、非同期でサーバに接続およびメッセージを送信している。<br>
<code>SslStream</code>クラスを使用して、SSL / TLS証明書に対応したセキュアな通信を実現している。<br>
<br>
また、タイムアウトが発生した場合の再試行処理や高度なエラー処理等も追加することを推奨する。<br>
<br>
<u>※注意</u><br>
<u>サーバ側でもSSL / TLS証明書の対応が必要となる。</u><br>
<u>実務では、適切な証明書の管理と定期的な更新が重要である。</u><br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
class SslAsyncTcpClientSender
{
    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 (SslStream sslStream = new SslStream(
                    client.GetStream(),
                    false,
                    new RemoteCertificateValidationCallback(SslCertificateValidator.ValidateServerCertificate),
                    null))
            {
                // SSL / TLS接続を確立
                await sslStream.AuthenticateAsClientAsync(server);
                // サーバへメッセージを送信
                string message = "Hello, Secure Server";
                byte[] data = Encoding.UTF8.GetBytes(message);
                var sendTask = sslStream.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 (AuthenticationException e)
      {  // SSL/TLS認証に失敗した場合
          Console.WriteLine($"AuthenticationException: SSL/TLS認証に失敗: {e.Message}");
      }
      catch (Exception e)
      {  // その他の予期せぬエラーが発生した場合
          Console.WriteLine($"予期せぬエラーが発生: {e.Message}");
      }
    }
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c#">
// ※注意
// 実務では、使用環境に応じて、さらに厳密な検証ロジックを追加することを推奨する。
// また、可能な限り信頼された認証局によって発行された証明書を使用することを推奨する。
//
// 信頼された証明書のリストは定期的に更新して、不要になった証明書は速やかに削除すること。
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public class SslCertificateValidator
{
    // 信頼された証明書のサムプリント
    // 実際の環境に合わせて更新すること
    private static readonly string[] TrustedCertificates = new string[]
    {
      "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0",
      "B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1"
    };
    public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
      if (sslPolicyErrors == SslPolicyErrors.None)
      {  // 証明書チェーンとポリシーエラーが無い場合
          LogMessage("情報", "証明書の検証に成功");
          return true;
      }
      LogMessage("警告", $"証明書エラー: {sslPolicyErrors}");
      if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors))
      {
          return HandleChainErrors(chain);
      }
      if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
      {
          LogMessage("エラー", "証明書の名前が不一致");
          return false;
      }
      if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable))
      {
          LogMessage("エラー", "リモート証明書が利用不可");
          return false;
      }
      // その他のエラーの場合
      return false;
    }
    private static bool HandleChainErrors(X509Chain chain)
    {
      foreach (X509ChainStatus status in chain.ChainStatus)
      {
          if (status.Status == X509ChainStatusFlags.UntrustedRoot)
          {
            X509Certificate2 rootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
            if (ValidateSelfSignedCertificate(rootCert))
            {
                LogMessage("情報", "信頼されたルート証明書を確認");
                return true;
            }
          }
          LogMessage("エラー", $"証明書チェーンエラー: {status.StatusInformation}");
      }
      return false;
    }
    private static bool ValidateSelfSignedCertificate(X509Certificate certificate)
    {
      string certHash = certificate.GetCertHashString();
      if (TrustedCertificates.Contains(certHash, StringComparer.OrdinalIgnoreCase))
      {
          LogMessage("情報", "信頼された自己署名証明書を確認");
          return true;
      }
      LogMessage("エラー", $"未知の自己署名証明書を確認  サムプリント: {certHash}");
      return false;
    }
    private static void LogMessage(string level, string message)
    {
      string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
      Console.WriteLine(logMessage);
      // ファイルやデータベースへのログ記録処理等を追加
    }
}
</syntaxhighlight>
<br>
==== 受信 ====
==== 受信 ====
以下の例では、TcpClientクラスを使用して、非同期でサーバに接続およびメッセージを受信している。<br>
以下の例では、TcpClientクラスを使用して、非同期でサーバに接続およびメッセージを受信している。<br>
171行目: 348行目:
           Console.WriteLine($"予期せぬエラーが発生: {e.Message}");
           Console.WriteLine($"予期せぬエラーが発生: {e.Message}");
       }
       }
    }
}
</syntaxhighlight>
<br>
==== 受信 (SSL / TLS対応) ====
以下の例では、SSL / TLS証明書を使用して、非同期でサーバに接続およびメッセージを受信している。<br>
また、タイムアウトが発生した場合の再試行処理や高度なエラー処理等も追加することを推奨する。<br>
<br>
<u>※注意</u><br>
<u>サーバ側でもSSL / TLS証明書の対応が必要となる。</u><br>
<u>実務では、適切な証明書の管理と定期的な更新が重要である。</u><br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
class SslAsyncTcpClientReceiver
{
    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 (SslStream sslStream = new SslStream(
                    client.GetStream(),
                    false,
                    new RemoteCertificateValidationCallback(SslCertificateValidator.ValidateServerCertificate),
                    null))
            {
                // SSL/TLS接続を確立
                await sslStream.AuthenticateAsClientAsync(server);
                // サーバからメッセージを受信
                byte[] buffer = new byte[4096];
                var receiveTask = sslStream.ReadAsync(buffer, 0, buffer.Length);
                if (await Task.WhenAny(receiveTask, Task.Delay(timeoutMilliseconds)) != receiveTask)
                {
                  throw new TimeoutException("エラー: メッセージの受信がタイムアウト");
                }
                int bytesRead = await receiveTask;
                string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                Console.WriteLine($"受信メッセージ: {receivedMessage}");
            }
          }
      }
      catch (SocketException e)
      {  // ネットワーク関連のエラーが発生した場合
          Console.WriteLine($"SocketException: {e.Message}");
      }
      catch (TimeoutException e)
      {  // タイムアウトが発生した場合
          Console.WriteLine($"TimeoutException: {e.Message}");
      }
      catch (AuthenticationException e)
      {  // SSL/TLS証明書の認証に失敗した場合
          Console.WriteLine($"AuthenticationException: SSL/TLS認証に失敗: {e.Message}");
      }
      catch (Exception e)
      {  // その他の予期せぬエラーが発生した場合
          Console.WriteLine($"予期せぬエラーが発生: {e.Message}");
      }
    }
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c#">
// ※注意
// 実務では、使用環境に応じて、さらに厳密な検証ロジックを追加することを推奨する。
// また、可能な限り信頼された認証局によって発行された証明書を使用することを推奨する。
//
// 信頼された証明書のリストは定期的に更新して、不要になった証明書は速やかに削除すること。
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public class SslCertificateValidator
{
    // 信頼された証明書のサムプリント
    // 実際の環境に合わせて更新すること
    private static readonly string[] TrustedCertificates = new string[]
    {
      "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0",
      "B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1"
    };
    public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
      if (sslPolicyErrors == SslPolicyErrors.None)
      {  // 証明書チェーンとポリシーエラーが無い場合
          LogMessage("情報", "証明書の検証に成功");
          return true;
      }
      LogMessage("警告", $"証明書エラー: {sslPolicyErrors}");
      if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors))
      {
          return HandleChainErrors(chain);
      }
      if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
      {
          LogMessage("エラー", "証明書の名前が不一致");
          return false;
      }
      if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable))
      {
          LogMessage("エラー", "リモート証明書が利用不可");
          return false;
      }
      // その他のエラーの場合
      return false;
    }
    private static bool HandleChainErrors(X509Chain chain)
    {
      foreach (X509ChainStatus status in chain.ChainStatus)
      {
          if (status.Status == X509ChainStatusFlags.UntrustedRoot)
          {
            X509Certificate2 rootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
            if (ValidateSelfSignedCertificate(rootCert))
            {
                LogMessage("情報", "信頼されたルート証明書を確認");
                return true;
            }
          }
          LogMessage("エラー", $"証明書チェーンエラー: {status.StatusInformation}");
      }
      return false;
    }
    private static bool ValidateSelfSignedCertificate(X509Certificate certificate)
    {
      string certHash = certificate.GetCertHashString();
      if (TrustedCertificates.Contains(certHash, StringComparer.OrdinalIgnoreCase))
      {
          LogMessage("情報", "信頼された自己署名証明書を確認");
          return true;
      }
      LogMessage("エラー", $"未知の自己署名証明書を確認  サムプリント: {certHash}");
      return false;
    }
    private static void LogMessage(string level, string message)
    {
      string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
      Console.WriteLine(logMessage);
      // ファイルやデータベースへのログ記録処理等を追加
     }
     }
  }
  }