C# TCP监听器高并发连接0.5%错误率的排查与优化方法
问题:TCP监听器高并发下出现连接拒绝错误(0.5%错误率)
我编写了一个简单的TCP监听器代码,使用Apache JMeter以500线程进行负载测试时,出现0.5%的错误率。错误信息显示:Response code: 500,Response message: java.net.ConnectException: Connection refused: connect。需要排查错误原因并优化该监听器解决问题。
代码如下:
static async Task Main(string[] args) { IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); int port = 7777; // Create a TCP listener TcpListener listener = new TcpListener(ipAddress, port); listener.Start(); Console.WriteLine("Server started. Waiting for connections..."); try { while (true) { TcpClient client = await listener.AcceptTcpClientAsync(); // Handle the client asynchronously _ = HandleClientAsync(client); } } finally { listener.Stop(); } } static async Task HandleClientAsync(TcpClient client) { try { NetworkStream stream = client.GetStream(); byte[] buffer = new byte[1024]; int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); string requestMessage = Encoding.ASCII.GetString(buffer, 0, bytesRead); // Console.WriteLine("Request message from client: " + requestMessage); // Response message byte[] responseData = Encoding.ASCII.GetBytes("test response"); //Thread.Sleep(50); // Send the response to the client asynchronously await stream.WriteAsync(responseData, 0, responseData.Length); //Console.WriteLine("Response sent to client."); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } finally { // Close the connection client.Close(); //Console.WriteLine("Client disconnected."); } }
附测试结果截图:
排查原因
- TCP连接等待队列不足:Windows默认的TCP半连接队列(backlog)容量较小(通常默认50),当500线程的并发请求超过队列承载上限时,新的连接请求会被系统直接拒绝。
- 资源释放不规范:手动调用
client.Close()可能存在资源释放不彻底的情况,导致连接资源积压,影响新连接建立。 - 线程池扩容延迟:异步操作依赖.NET线程池,高并发场景下线程池默认的扩容机制可能跟不上请求速度,导致
AcceptTcpClientAsync无法及时处理新连接。 - 系统TCP参数限制:系统层面的TCP连接相关参数(如
MaxConnBacklog)未调整,无法支持高并发连接请求。
优化方案
1. 增大TCP连接等待队列
修改TcpListener.Start()方法,显式指定更大的backlog值,适配500线程的并发需求:
listener.Start(1000); // 设置队列大小为1000,可根据实际并发量调整
同时调整Windows系统TCP参数(管理员权限执行命令提示符):
netsh int tcp set global maxconnbacklog=2000 netsh int tcp set global synattackprotect=disabled
修改后需重启系统生效,参数值可根据硬件配置进一步调整。
2. 规范资源释放逻辑
使用using语句自动管理TcpClient和NetworkStream的生命周期,替代手动Close(),确保资源被及时释放:
static async Task HandleClientAsync(TcpClient client) { using (client) using (NetworkStream stream = client.GetStream()) { byte[] buffer = new byte[1024]; int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); string requestMessage = Encoding.ASCII.GetString(buffer, 0, bytesRead); byte[] responseData = Encoding.ASCII.GetBytes("test response"); await stream.WriteAsync(responseData, 0, responseData.Length); await stream.FlushAsync(); // 确保响应数据完全发送 } }
3. 优化线程池配置
在程序启动时设置线程池最小线程数,避免高并发下线程池扩容延迟:
static async Task Main(string[] args) { // 配置线程池最小工作线程和IO线程数,适配高并发场景 ThreadPool.SetMinThreads(200, 200); IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); int port = 7777; TcpListener listener = new TcpListener(ipAddress, port); listener.Start(1000); Console.WriteLine("Server started. Waiting for connections..."); try { while (true) { TcpClient client = await listener.AcceptTcpClientAsync(); _ = HandleClientAsync(client); } } finally { listener.Stop(); } }
4. 添加连接监控日志
在关键节点添加日志,便于定位连接失败的具体场景:
static async Task HandleClientAsync(TcpClient client) { var clientAddr = client.Client.RemoteEndPoint.ToString(); try { using (client) using (NetworkStream stream = client.GetStream()) { Console.WriteLine($"新连接建立:{clientAddr}"); byte[] buffer = new byte[1024]; int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); byte[] responseData = Encoding.ASCII.GetBytes("test response"); await stream.WriteAsync(responseData, 0, responseData.Length); await stream.FlushAsync(); Console.WriteLine($"响应已发送:{clientAddr}"); } } catch (Exception ex) { Console.WriteLine($"连接处理出错 {clientAddr}:{ex.Message}"); } }
内容的提问来源于stack exchange,提问作者Kamil Ibadov
相关产品推荐
相关产品推荐

