Created
December 6, 2025 05:21
-
-
Save sakusaku3/83e2a824aec925e078bd5aaa6a506cd5 to your computer and use it in GitHub Desktop.
csharp_tspclient.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Net.Sockets; | |
| using System.Text; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| public class CancelableTcpClient | |
| { | |
| // CancellationTokenSourceをクラスレベルで保持 | |
| private readonly CancellationTokenSource _cts = new CancellationTokenSource(); | |
| public async Task StartAsyncClient(string host, int port, string message) | |
| { | |
| // CancellationTokenを取得 | |
| CancellationToken token = _cts.Token; | |
| try | |
| { | |
| // 接続とデータ送信の処理 (前回の例から簡略化) | |
| using (var client = new TcpClient()) | |
| { | |
| // **【ポイント1】接続処理のキャンセル対応** | |
| // ConnectAsyncには直接CancellationTokenを渡せないため、Task.Delayと組み合わせる | |
| var connectTask = client.ConnectAsync(host, port); | |
| // 接続を待機。同時にキャンセル要求やタイムアウト(例として5秒)も監視 | |
| var completedTask = await Task.WhenAny(connectTask, Task.Delay(5000, token)); | |
| if (completedTask != connectTask) | |
| { | |
| // Task.Delayか、またはTask.Delayに渡されたトークンがキャンセルされた | |
| token.ThrowIfCancellationRequested(); // ここでキャンセルならOperationCanceledException | |
| throw new TimeoutException("接続処理がタイムアウトしました。"); | |
| } | |
| // 接続処理を完了 | |
| client.EndConnect(connectTask.GetAwaiter().GetResult()); | |
| Console.WriteLine($"サーバー {host}:{port} に接続しました。"); | |
| using (NetworkStream stream = client.GetStream()) | |
| { | |
| // 送信処理(WriteAsyncにトークンを渡す) | |
| byte[] data = Encoding.UTF8.GetBytes(message); | |
| Console.WriteLine("送信中..."); | |
| await stream.WriteAsync(data, 0, data.Length, token); | |
| Console.WriteLine("送信完了。"); | |
| // **【ポイント2】長時間待機するReadAsyncにトークンを渡す** | |
| byte[] buffer = new byte[256]; | |
| Console.WriteLine("受信待機中... (Qキーでキャンセル)"); | |
| // ReadAsyncにトークンを渡す | |
| int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token); | |
| string response = Encoding.UTF8.GetString(buffer, 0, bytesRead); | |
| Console.WriteLine($"受信完了: {response}"); | |
| } | |
| } | |
| } | |
| // **【ポイント3】キャンセル時の例外を捕捉** | |
| catch (OperationCanceledException) | |
| { | |
| // CancellationTokenSource.Cancel() が呼ばれた際にここが実行される | |
| Console.WriteLine("\n--- 通信処理がユーザーによってキャンセルされました。---"); | |
| } | |
| catch (TimeoutException e) | |
| { | |
| Console.WriteLine($"タイムアウトエラー: {e.Message}"); | |
| } | |
| catch (Exception e) | |
| { | |
| Console.WriteLine($"エラーが発生しました: {e.Message}"); | |
| } | |
| } | |
| // ユーザーからのキャンセルを受け付けるメソッド | |
| public void ListenForCancel() | |
| { | |
| // 処理が既にキャンセルされていないか確認 | |
| if (_cts.IsCancellationRequested) return; | |
| // Qキーが押されるまでコンソール入力を待つ | |
| while (Console.ReadKey(true).Key != ConsoleKey.Q) | |
| { | |
| // Qキー以外の場合は何もしない | |
| } | |
| // キャンセル要求を発行する | |
| _cts.Cancel(); | |
| } | |
| } | |
| using System.Threading.Tasks; | |
| public class Program | |
| { | |
| public static async Task Main(string[] args) | |
| { | |
| var client = new CancelableTcpClient(); | |
| // 1. 通信タスクを起動 | |
| var clientTask = client.StartAsyncClient("127.0.0.1", 8000, "Cancel Test"); | |
| // 2. キャンセル監視タスクを起動 | |
| var listenTask = Task.Run(() => client.ListenForCancel()); | |
| // 3. 両方のタスクの完了を待機 | |
| await Task.WhenAll(clientTask, listenTask); | |
| Console.WriteLine("アプリケーション終了。"); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment