Skip to content

Instantly share code, notes, and snippets.

@sakusaku3
Created December 6, 2025 05:21
Show Gist options
  • Select an option

  • Save sakusaku3/83e2a824aec925e078bd5aaa6a506cd5 to your computer and use it in GitHub Desktop.

Select an option

Save sakusaku3/83e2a824aec925e078bd5aaa6a506cd5 to your computer and use it in GitHub Desktop.
csharp_tspclient.cs
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