You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    }
}

疑问

  1. Listen()中设置的backlog值大于并发数,为何似乎无效?
  2. 如何成功处理并发请求?

问题分析与解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:21:10