.NET Socket Listen backlog未正常工作:10k并发连接受限问题
无法实现10000个TCP并发连接的问题
我正在测试一个用C#编写的简单TCP服务器,目标是支持10000个客户端并发连接。已将Socket.Listen的backlog参数设为int.MaxValue,但测试时仅约2000个客户端能成功连接,其余均抛出ConnectionRefused错误,调整backlog值也无改善。
服务器代码
public class HighPerfTcpServer { private Socket? _listeningSocket; private ILogger<HighPerfTcpServer> _logger; public HighPerfTcpServer(ILogger<HighPerfTcpServer> logger) { _logger = logger; } public async Task<int> Start(TcpServerOptions options, ITcpServerSessionsHandler sessionHandler) { var address = options.Address ?? "127.0.0.1"; var port = options.Port ?? 0; var ipAddress = IPAddress.Parse(address); var localEndPoint = new IPEndPoint(ipAddress, port); _listeningSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _listeningSocket.Bind(localEndPoint); _listeningSocket.Listen(int.MaxValue); var acceptArgs = new SocketAsyncEventArgs(); acceptArgs.Completed += AcceptArgs_Completed; StartAccept(acceptArgs); var currentLocalEndPoint = _listeningSocket.LocalEndPoint as IPEndPoint; ArgumentNullException.ThrowIfNull(currentLocalEndPoint); return currentLocalEndPoint.Port; } private void StartAccept(SocketAsyncEventArgs acceptArgs) { if (_listeningSocket == null) { return; } var willRaiseEvent = false; while (!willRaiseEvent) { acceptArgs.AcceptSocket = null; try { willRaiseEvent = _listeningSocket.AcceptAsync(acceptArgs); if (!willRaiseEvent) { ProcessAccept(acceptArgs); } } catch { break; } } } private void ProcessAccept(SocketAsyncEventArgs acceptArgs) { _logger.LogDebug($"client connected"); } private void AcceptArgs_Completed(object? sender, SocketAsyncEventArgs e) { if (e.LastOperation == SocketAsyncOperation.Accept) { ProcessAccept(e); StartAccept(e); } } public async Task Stop() { _listeningSocket?.Dispose(); } }
客户端代码
public class HighPerfTcpClient { private TaskCompletionSource? _connectAwaiter; private TaskCompletionSource? _disconnectAwaiter; private Socket? _clientSocket; public async Task<ITcpClientConnection> Connect(TcpClientOptions options) { ArgumentNullException.ThrowIfNull(options.Address); ArgumentNullException.ThrowIfNull(options.Port); var ipAddress = IPAddress.Parse(options.Address); var remoteEndPoint = new IPEndPoint(ipAddress, options.Port.Value); _clientSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); var connectEventArgs = new SocketAsyncEventArgs(); connectEventArgs.Completed += ConnectEventArgs_Completed; connectEventArgs.RemoteEndPoint = remoteEndPoint; _connectAwaiter = new TaskCompletionSource(); var willRaiseEvent = _clientSocket.ConnectAsync(connectEventArgs); if (willRaiseEvent) { await _connectAwaiter.Task.ConfigureAwait(false); } if (connectEventArgs.SocketError != SocketError.Success) { throw new Exception($"socket connect error: {connectEventArgs.SocketError}"); } return new TcpClientConnection(DataSender); } private async Task DataSender(ArraySegment<byte> segment) { } private void ConnectEventArgs_Completed(object? sender, SocketAsyncEventArgs e) { if (e.LastOperation == SocketAsyncOperation.Connect) { _connectAwaiter?.SetResult(); } } public async Task Disconnect() { if (_clientSocket != null) { var disconnectArgs = new SocketAsyncEventArgs(); disconnectArgs.Completed += DisconnectArgs_Completed; _disconnectAwaiter = new TaskCompletionSource(); var willRaiseEvent = _clientSocket.DisconnectAsync(disconnectArgs); if (willRaiseEvent) { await _disconnectAwaiter.Task.ConfigureAwait(false); } } } private void DisconnectArgs_Completed(object? sender, SocketAsyncEventArgs e) { if (e.LastOperation == SocketAsyncOperation.Disconnect) { _disconnectAwaiter?.SetResult(); } } }
测试用例代码
public class TcpClientServerIntegrationTests { private static ILoggerFactory _factory; [Fact] public async Task Should_be_able_to_up_server_and_clients_connect_and_disconnect() { var testLogger = GetLogger<TcpClientServerIntegrationTests>(); var sessionHandlerMock = new Mock<ITcpServerSessionsHandler>(); var server = new HighPerfTcpServer(GetLogger<HighPerfTcpServer>()); var connections = 10 * 1000; var port = 10000; var tcpServerOptions = new TcpServerOptions { Address = "127.0.0.1", ConnectionBufferSize = 128 * 1024, MaxConnections = connections, Port = port }; var startedPort = await server.Start(tcpServerOptions, sessionHandlerMock.Object); var tasks = Enumerable.Range(0, connections).Select(e => { return Task.Run(async () => { var client = new HighPerfTcpClient(); var clientOptions = new TcpClientOptions { Address = "127.0.0.1", Port = startedPort, BufferSize = 128 * 1024, OnData = OnClientData, OnDisconnect = OnDisconnect }; await client.Connect(clientOptions); testLogger.LogDebug($"{e} client connected"); await client.Disconnect(); testLogger.LogDebug($"{e} client disconnected"); }); }); await Task.WhenAll(tasks); } private async Task OnDisconnect() { } private async Task OnClientData(ArraySegment<byte> segment) { } private ILogger<T> GetLogger<T>() { return _factory.CreateLogger<T>(); } static TcpClientServerIntegrationTests() { var configuration = new LoggingConfiguration(); var target = new FileTarget(); target.FileName = "./logs/log.${logger}.txt"; target.KeepFileOpen = false; configuration.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Error, target); _factory = LoggerFactory.Create(builder => { builder.AddNLog(configuration); }); } }
问题原因及解决方案
1. backlog参数的实际限制
设置的int.MaxValue不会被系统完全采纳,操作系统对backlog有内置上限:
- Windows系统默认将
backlog截断为200(部分新版本提升至16384),超出部分会被忽略。 backlog仅控制**未完成连接队列(SYN_RCVD状态)**的大小,**已完成连接队列(ESTABLISHED状态)**的容量由系统独立参数控制,若服务器不及时处理已完成连接,队列满后会直接拒绝新连接。
2. 服务器未处理已接受的Socket
ProcessAccept方法仅打印日志,未对acceptArgs.AcceptSocket做任何处理:
- 每个已接受的Socket会占用系统文件句柄,Windows进程默认句柄上限约为10000,短时间内大量未释放的Socket会快速耗尽资源,导致后续无法Accept新连接。
- 未关闭的Socket会维持ESTABLISHED状态,持续占用TCP连接资源,进一步压缩并发空间。
3. 客户端临时端口耗尽
客户端连接时使用系统分配的临时端口(Windows默认范围49152-65535,共16384个):
- 测试中短时间创建10000个连接,即使客户端调用
Disconnect,Socket仍会进入TIME_WAIT状态(默认持续240秒),临时端口无法立即复用,导致端口耗尽,后续连接失败。
4. 系统TCP参数限制
Windows默认TCP参数对并发连接有诸多限制,例如:
MaxUserPort:临时端口范围上限,默认值偏小。TcpTimedWaitDelay:TIME_WAIT状态持续时间,默认240秒过长。TcpNumConnections:系统允许的最大TCP连接数,默认值可能不足。
解决方案
正确处理服务器的已接受Socket
在ProcessAccept中关闭或管理Socket(若无需保持连接):private void ProcessAccept(SocketAsyncEventArgs acceptArgs) { _logger.LogDebug($"client connected"); // 释放已接受的Socket资源 acceptArgs.AcceptSocket?.Close(); acceptArgs.AcceptSocket?.Dispose(); }调整系统TCP参数(Windows)
通过注册表修改以下参数(需管理员权限):HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort:设为65535HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay:设为30(秒)HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpNumConnections:设为65535
优化客户端连接逻辑
避免短时间内并发创建大量连接,可采用连接池复用,或增加连接间隔。调整进程句柄限制
在Windows任务管理器中,找到测试进程的“详细信息”,右键选择“设置优先级”->“详细信息”,调整“句柄数”上限(或通过组策略修改)。
内容的提问来源于stack exchange,提问作者Babu James
相关产品推荐
相关产品推荐

