示例中作为TCP套接字服务器的Worker Service如何正确关闭
问题根因
- 核心原因1:
clientServer.start方法未关联服务停止的取消令牌,TCP监听逻辑处于永久阻塞状态。当服务收到停止指令时,ExecuteAsync无法跳出阻塞响应stoppingToken的取消信号,宿主等待超时后会强制终止进程,StopAsync方法没有执行机会,Socket资源未被释放。 - 核心原因2:Socket实例作用域混乱,Main方法中提前创建了无用的Socket实例占用端口,Worker类中又单独创建了新的SocketConnection实例,两处Socket实例没有统一管理。
- 辅助原因:服务未初始化成功时,
ExecuteAsync会进入无延迟的空跑死循环,占用CPU资源的同时也会影响停止信号的响应。
修复方案
1. 清理无效Socket初始化
删除Main方法中提前创建的无用Socket实例,避免提前占用目标端口:
public static void Main(string[] args) { Log log = new(); // 移除该行无用的Socket创建逻辑 // Socket socket = new SocketConnection().getSocket(); try { DataStorage.Instance.osPlatform = RuntimeInformation.OSDescription; if (DataStorage.Instance.osPlatform.Contains("Win")) { Console.WriteLine("WinOS"); CreateHostBuilderWin(args).Build().Run(); } else { Console.WriteLine("MacOS"); CreateHostBuilderMac(args).Build().Run(); } } catch (Exception ex) { log.write("Could not start SDS. ", ex); } log.write("Program is reached on shutdown", null); }
2. 改造clientServer.start方法支持取消信号
给start方法新增CancellationToken入参,监听逻辑中关联取消令牌,收到停止信号时自动跳出阻塞:
// clientServer.start方法改造示例 public async Task start(Socket listener, CancellationToken stoppingToken) { // 开启端口复用,避免异常退出后端口占用 listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); listener.Listen(); while (!stoppingToken.IsCancellationRequested) { // 使用支持取消令牌的AcceptAsync重载 var client = await listener.AcceptAsync(stoppingToken); // 原有客户端处理逻辑 } }
3. 优化Worker执行逻辑,增加资源释放双保险
在ExecuteAsync中传入取消令牌、增加异常延迟,同时在finally块中补充Socket释放逻辑,和StopAsync形成双重保障:
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try{ while (!stoppingToken.IsCancellationRequested) { try { if (DataStorage.Instance.sdsInitialized) { log.write("startasync is reached", null); listener = socket.getSocket(); log.write("Socket: " + listener.AddressFamily + " " + listener.RemoteEndPoint, null); // 传入停止取消令牌 await clientServer.start(listener, stoppingToken); } else { log.write("Sds not initialized properly", null); // 增加延迟避免空跑循环 await Task.Delay(1000, stoppingToken); } } catch (OperationCanceledException) { log.write("Service cancellation requested, exiting", null); break; } catch (Exception ex) { log.write("Could not create Socket for server", ex); // 异常后增加延迟避免崩溃循环 await Task.Delay(3000, stoppingToken); } } } finally { log.write("Finally reached, releasing socket resource", null); // 补充释放逻辑,双保险 if (listener != null) { EndSDS endSDS = new EndSDS(); endSDS.start(listener); } _hostApplicationLifetime.StopApplication(); } }
内容的提问来源于stack exchange,提问作者Kim Sandberg
相关产品推荐
相关产品推荐

