Windows-C# TCP服务器连接被拒绝问题及并发处理求助
关于C# TCP服务器高并发连接失败的问题与解决方案
问题场景
我使用C#编写了如下TCP服务器程序,运行后用JMeter在1秒内发起1000线程的测试(已将backlog设置为10000),但仅约800个连接成功建立,其余均报错“connection refused”,恳请各位提供建议。
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp1 { /// <summary> /// MainWindow.xaml /// </summary> public partial class MainWindow : Window { private Socket listenSocket; private static ArrayList g_fdlist = new ArrayList(); public MainWindow() { InitializeComponent(); } private void btn_confirm_Click(object sender, RoutedEventArgs e) { var port = Convert.ToInt32(tb_port.Text); var backlog = Convert.ToInt32(backlogSize.Text); start_socket_server(port, backlog); //start_socket_server_selectmode(port, backlog); } #region select private void start_socket_server_selectmode(int port, int backlog) { if (listenSocket != null) { listenSocket.Close(); listenSocket = null; } IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port); listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(localEndPoint); listenSocket.Listen(backlog); System.Console.WriteLine(fetchcurrentTime() + ": Start Listen in " + port + ", " + backlog); Thread t = new Thread(new ThreadStart(workThread)); t.Start(); while (true) { Socket s = listenSocket.Accept(); System.Console.WriteLine(fetchcurrentTime() + ": Accept connect " + s.RemoteEndPoint.ToString()); g_fdlist.Add(s); } } private void workThread() { ArrayList selectFDlist = new ArrayList(); while (true) { selectFDlist.Clear(); for (int i=0; i<g_fdlist.Count; ++i) { selectFDlist.Add(g_fdlist[i]); } if (selectFDlist.Count <= 0) { continue; } Socket.Select(selectFDlist, null, null, 1); int num = selectFDlist.Count; byte[] buf = new byte[100]; foreach(Socket s in selectFDlist) { int retlen = s.Receive(buf); if(retlen > 0) { System.Console.WriteLine(fetchcurrentTime() + ": Receive data from " + s.RemoteEndPoint.ToString()); Thread.Sleep(10000); } else { System.Console.WriteLine(fetchcurrentTime() + ": socket is closed. " + s.RemoteEndPoint.ToString()); s.Shutdown(SocketShutdown.Both); s.Close(); g_fdlist.Remove(s); } } } } #endregion select #region Socket Listen->Accept->Receive->Process private void start_socket_server(int port, int backlog) { if (listenSocket != null) { listenSocket.Close(); listenSocket = null; } IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port); listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(localEndPoint); listenSocket.Listen(backlog); System.Console.WriteLine(fetchcurrentTime() + ": Start Listen in " + port + ", " + backlog); StartAccept(); } internal string fetchcurrentTime() { return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff:ffffff"); } internal void StartAccept() { System.Console.WriteLine(fetchcurrentTime() + ": StartAccecpt. ================="); SocketAsyncEventArgs acceptEventArg = CreateNewSaeaForAccept(); try { bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { ProcessAccept(acceptEventArg); } } catch (Exception ex) { System.Console.WriteLine(fetchcurrentTime() + ": StartAccept" + ex.Message + "\r\n" + ex.StackTrace); StartAccept(); } } private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) { ProcessAccept(e); } internal SocketAsyncEventArgs CreateNewSaeaForAccept() { //Allocate the SocketAsyncEventArgs object. var acceptEventArg = new SocketAsyncEventArgs(); acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed); return acceptEventArg; } private void ProcessAccept(SocketAsyncEventArgs acceptEventArgs) { System.Console.WriteLine(fetchcurrentTime() + ": ProcessAccept. ++++++++++++++"); if (acceptEventArgs.SocketError != SocketError.Success) { System.Console.WriteLine(fetchcurrentTime() + ": connect error:acceptEventArgs.SocketError != SocketError.Success,SocketErrorType:" + acceptEventArgs.SocketError.ToString()); StartAccept(); return; } StartAccept(); System.Console.WriteLine(fetchcurrentTime() + ": Receive Connect " + acceptEventArgs.AcceptSocket.RemoteEndPoint.ToString()); Thread.Sleep(30000); SocketAsyncEventArgs tupleReceiveSendEventArgs = new SocketAsyncEventArgs(); tupleReceiveSendEventArgs.Completed += IO_Completed; tupleReceiveSendEventArgs.AcceptSocket = acceptEventArgs.AcceptSocket; acceptEventArgs.AcceptSocket = null; StartReceive(tupleReceiveSendEventArgs); } void IO_Completed(object sender, SocketAsyncEventArgs e) { // determine which type of operation just completed and call the associated handler switch (e.LastOperation) { case SocketAsyncOperation.Receive: { ProcessReceive(e); break; } case SocketAsyncOperation.Send: { break; } default: { //This exception will occur if you code the Completed event of some //operation to come to this method, by mistake. throw new ArgumentException("The last operation completed on the socket was not a receive or send"); } } } private void StartReceive(SocketAsyncEventArgs receiveEventArgs) { try {//debug byte[] buff = new byte[100]; if (receiveEventArgs.AcceptSocket.Connected) { receiveEventArgs.AcceptSocket.Receive(buff); } } catch (Exception ex) { System.Console.WriteLine(fetchcurrentTime() + ": StartReceive" + "\r\n" + ex.Message + "\r\n" + ex.StackTrace); } } private void ProcessReceive(SocketAsyncEventArgs receiveEventArgs) { try { if (receiveEventArgs.SocketError != SocketError.Success) { Console.WriteLine(fetchcurrentTime() + ": Process Receive SocketError."); return; } if (receiveEventArgs.BytesTransferred == 0) { Console.WriteLine(fetchcurrentTime() + ": Process Receive BytesTransferred is 0."); return; } Int32 remainingBytesToProcess = receiveEventArgs.BytesTransferred; int availableBytes = remainingBytesToProcess; var byteArrayIn = new byte[availableBytes]; Buffer.BlockCopy(receiveEventArgs.Buffer, 0, byteArrayIn, 0, remainingBytesToProcess); StartReceive(receiveEventArgs); } catch (Exception ex) { Console.WriteLine(fetchcurrentTime() + ": ProcessReceive" + ex.Message + "\r\n" + ex.StackTrace); } } #endregion Socket Listen->Accept->Receive->Process } }
疑问
- Listen()中设置的backlog值大于并发数,为何似乎无效?
- 如何成功处理并发请求?
问题分析与解决方案
1. backlog看似失效的原因
backlog不是简单的“最大连接数”,它控制的是TCP半连接队列(未完成三次握手的请求)+ 全连接队列(已完成三次握手但未被Accept的连接)的总和上限,但实际有两个关键限制:
- 系统内核会截断backlog值:Windows默认全连接队列上限远低于10000,即使你设置了大值,内核也会按自身限制生效。
- 代码阻塞导致队列堆积:
ProcessAccept里的Thread.Sleep(30000)会让处理连接的线程阻塞30秒,全连接队列里的连接无法被及时消费,新请求因队列满被拒绝。
2. 处理并发请求的优化方案
(1)移除致命阻塞操作
立刻删除ProcessAccept中的Thread.Sleep(30000),这个操作直接导致连接处理停滞,是高并发失败的核心原因之一。
(2)复用SocketAsyncEventArgs对象
当前每次Accept都创建新的SocketAsyncEventArgs,高频并发下会引发大量GC和内存开销,建议维护一个对象池复用这些实例,减少资源消耗。
(3)调整Windows系统TCP参数
修改注册表增大连接相关限制(修改后需重启系统):
- 定位到
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters - 添加/修改:
TcpMaxConnResponseDelay:设为0,减少连接响应延迟MaxUserPort:设为65534,增大可用客户端端口范围TcpNumConnections:增大系统总TCP连接数上限
(4)异步化接收逻辑
当前StartReceive用了同步Receive调用,会阻塞线程,改用异步ReceiveAsync:
private void StartReceive(SocketAsyncEventArgs receiveEventArgs) { try { byte[] buff = new byte[100]; receiveEventArgs.SetBuffer(buff, 0, buff.Length); bool willRaiseEvent = receiveEventArgs.AcceptSocket.ReceiveAsync(receiveEventArgs); if (!willRaiseEvent) { ProcessReceive(receiveEventArgs); } } catch (Exception ex) { System.Console.WriteLine(fetchcurrentTime() + ": StartReceive" + "\r\n" + ex.Message + "\r\n" + ex.StackTrace); } }
(5)修复线程安全问题
g_fdlist使用非线程安全的ArrayList,多线程操作会引发异常,改用ConcurrentBag<Socket>或者在操作时加锁保护。
内容的提问来源于stack exchange,提问作者Theodore
相关产品推荐
相关产品推荐

