c# - 在 .net 中处理大量 TCP 客户端连接

标签 c# .net sockets networking tcp

我需要构建一个应用程序(服务器)来处理通过 TCP 从客户端发送的数据。我必须能够支持(至少)2000 个连接。我已经尝试编写 TCP 服务器,但我发现当我开始达到 600/700 个连接时,我的服务器的响应速度大大减慢(它实际上随着时间的推移随着接收到越来越多的连接而减慢)。我通常不编写网络代码,所以我确信有很多概念我没有完全理解并且可以改进。

我的服务器的主要目的是:

  1. 处理从客户端发送的数据并将其存储在 sql 数据库中。
  2. 决定(基于 根据收到的最后一条消息)对客户端的正确响应应该是什么。
  3. 排队响应列表和 一次一个地发送给客户。

所有客户都需要这样做。下面是我实现的代码:

    private readonly TcpListener tcpListener;
    private readonly Thread listenThread;    
    private bool run = true;

    public Server()
    {
         this.tcpListener = new TcpListener(IPAddress.Any, AppSettings.ListeningPort); //8880 is default for me.
         this.listenThread = new Thread(new ThreadStart(ListenForClients));
         this.listenThread.Start();
    }

    private void ListenForClients()
    {
         this.tcpListener.Start();

         while (run) {
              TcpClient client = this.tcpListener.AcceptTcpClient();

              //create a thread to handle communication with connected client
              Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
              clientThread.Start(client);
          }
    }
    private void HandleClientComm(object client)
    {
        Queue responseMessages = new Queue();
        while (run)
        {
            try
            {
                lastMessage = clientStream.GetMessage();

                if (lastMessage.Length > 0)
                {
                    // Process logic here...  
                    //an item may be added to the response queue, depending on logic.
                }

                if (responseMessages.Count > 0)
                {
                   clientStream.WriteMessage(msg);
                   clientStream.Flush();

                   // sleep for 250 milliseconds (or whats in the config).
                   Thread.Sleep(AppSettings.MessageSendDelayMilliseconds); 
                }
            }
            catch (Exception ex)
            {
                break;
            }
        }
        tcpClient.Close();
    }

最后,这是我编写的一个扩展类来帮助我解决问题:

namespace System.Net.Sockets
{
    using System.Text;

    public static class NetworkSteamExtension
    {
        private static readonly ASCIIEncoding Encoder = new ASCIIEncoding();

        public static string GetMessage(this NetworkStream clientStream)
        {
            string message = string.Empty;
            try
            {
                byte[] bMessage = new byte[4068];
                int bytesRead = 0;
                while (clientStream.DataAvailable)
                {
                    bytesRead = clientStream.Read(bMessage, 0, 4068);
                    message += Encoder.GetString(bMessage, 0, bytesRead);
                }
            }
            catch (Exception)
            {

            }
            return message;         
        }

        public static void WriteMessage(this NetworkStream clientStream, string message)
        {
            byte[] buffer = Encoder.GetBytes(message);
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
        }
    }
}

很多关于这个主题的文章人们都在使用套接字。我还读到 .net 4.5 async/await 是实现解决方案的最佳方式。

我想确保利用 .net 中的最新功能(如果有帮助,甚至是 4.5.2)并构建一个至少可以处理 2000 个连接的服务器。有人可以建议吗?

非常感谢!

最佳答案

好的,我们需要修复一些 API 使用错误,然后是创建过多线程的主要问题。众所周知,许多连接只能通过异步 IO 有效处理。数百个连接算作“太多”。

您的客户端处理循环必须是异步的。重写 HandleClientComm,使其使用异步套接字 IO。这很容易等待。您需要在网上搜索“.net socket await”。

您可以继续使用同步接受调用。那里没有缺点。保留简单的同步代码。仅使那些具有重要 avgWaitTime * countPerSecond 产品的调用异步。通常,这将是所有套接字调用。

您假设 DataAvailable 返回任何给定消息中的字节数。 TCP 不保留消息边界。你需要自己做。 DataAvailable 值几乎没有意义,因为它可能低估了 future 将到达的真实数据。

通常使用更高级别的序列化协议(protocol)会更好。例如,带有长度前缀的 protobuf。不要使用 ASCII。您可能已经这样做了,因为您不知道如何使用“真实”编码来做到这一点。

使用 using 处理所有资源。不要使用非泛型 Queue 类。不要刷新流,这对套接字没有任何作用。

你为什么要 sleep ?

关于c# - 在 .net 中处理大量 TCP 客户端连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31904039/

相关文章:

c# - 正则表达式仅匹配单词中的第一个字母

c# - 对每个类单独的 MVVM 命令

c# - Rx.NET 中是否有一个 Subject 实现在功能上类似于 BehaviorSubject 但仅在值发生变化时才会发出?

c# - C++/命令行界面 : why should I use it?

java - 程序在 ObjectInputStream 阻塞

c# - 如何从当前执行的方法中获取 BackgroundWorker 的实例?

c# - 如何检查 wpf C# 中的鼠标按钮是左键还是右键?

c# - 如何使用 C# 绑定(bind) .aspx 中定义的现有下拉列表

c - 哪个套接字,clientSocket = accept() 或 listen(socket),你设置了 sockopt SO_KEEPALIVE?

c# - 用于读取和解码 SSL 消息的循环不起作用