【问题标题】:Question about listening and backlog for sockets关于so​​ckets监听和积压的问题
【发布时间】:2020-09-17 03:05:23
【问题描述】:

我正在用 C# 编写一个需要处理传入连接的应用程序,而我以前从未做过服务器端编程。这让我想到了以下问题:

  • 高积压/低积压的利弊?为什么我们不应该将积压工作设置为一个巨大的数字?
  • 如果我调用 Socket.Listen(10),在 10 次 Accept() 之后我是否必须再次调用 Listen()?还是我必须在每次 Accept() 之后调用 Listen()?
  • 如果我将积压工作设置为 0,并且假设有两个人想同时连接到我的服务器,会发生什么情况? (我在循环中调用 Socket.Select 并检查侦听套接字的可读性,在我处理第一个连接后,如果我再次调用 Listen(),第二个连接是否会在下一次迭代时成功?)

提前致谢。

【问题讨论】:

    标签: c# sockets server-side


    【解决方案1】:

    listen backlog 是 as Pieter said 一个队列,操作系统使用该队列来存储 TCP 堆栈已接受但您的程序尚未接受的连接。从概念上讲,当客户端连接时,它将被放置在此队列中,直到您的 Accept() 代码将其删除并将其交给您的程序。

    因此,listen backlog 是一个调整参数,可用于帮助您的服务器处理并发连接尝试中的峰值。请注意,这与并发连接尝试的峰值有关,与您的服务器可以维护的最大并发连接数无关。例如,如果您的服务器每秒接收 10 个新连接,那么即使这些连接长期存在并且您的服务器支持 10,000 个并发连接(假设您的服务器没有达到最大值),调整侦听积压也不太可能产生任何影响退出为现有连接提供服务的 CPU!)。但是,如果服务器在每秒接受 1000 个新连接时偶尔会遇到短时间,那么您可以通过调整侦听积压以提供更大的队列来防止某些连接被拒绝,从而让您的服务器有更多时间调用Accept()每个连接。

    至于优点和缺点,优点是您可以更好地处理并发连接尝试中的峰值,而相应的缺点是操作系统需要为侦听积压队列分配更多空间,因为它更大。所以这是性能与资源的权衡。

    就我个人而言,我将监听积压工作设置为可以通过配置文件进行外部调整的内容。

    如何以及何时调用listen 和accept 取决于您使用的套接字代码的风格。使用同步代码,您将调用 Listen() 一次,其值为 10,用于监听积压,然后循环调用 Accept()。监听调用设置您的客户端可以连接的端点,并在概念上创建指定大小的监听积压队列。调用Accept() 会从listen backlog 队列中删除一个挂起的连接,设置一个socket 供应用程序使用,并将它作为新建立的连接传递给您的代码。如果您的代码调用Accept()、处理新连接并循环调用Accept() 所花费的时间比并发连接尝试之间的间隔长,那么您将开始在监听积压队列中累积条目。

    使用异步套接字可能会有些不同,如果您使用异步接受,您将像以前一样侦听一次,然后发布几个(再次可配置的)异步接受。随着这些中的每一个完成,您将处理新连接并发布新的异步接受。通过这种方式,您有一个侦听积压队列和一个待处理的接受“队列”,因此您可以更快地接受连接(此外,异步接受是在线程池线程上处理的,因此您没有一个紧密的接受循环)。这通常更具可扩展性,并为您提供两点调整以处理更多并发连接尝试。

    【讨论】:

    • 嗨,我正在尝试编写测试...而代码不是...明确是什么问题...我正在循环超过 10 000 个客户端请求...每个连接都进行自己的tcp连接。我已将积压工作设置为 20 000。侦听器正在接受异步连接并将它们放入自己的线程中。我已经告诉听众每个接受的连接做 5 秒的工作。几乎立刻我就被客户拒绝了……我知道这不是真实的世界测试……我只是想了解所有的限制……你能帮助我理解吗……
    • 1) 您最好写一个问题,而不是在现有答案中添加模糊的评论。 2) 使用“每个连接线程”模型扩展到 10,000 并不理想。 3)这种测试可能会遇到各种系统资源限制,具体取决于它的结构(TIME_WAIT)是其中之一。 4) 20,000 的积压只是说“允许 20,000 个连接尝试挂起”它不会影响可能有多少活动连接,20,000 是一个非常大的数字,可能比操作系统允许的更大...... 5)您可能做错了很多事情,我需要查看代码
    • @LenHolgate 客户端使用发送/写入调用发出的数据请求是否也在监听积压队列中排队?在从客户端代码调用发送/写入时,我们将服务器的侦听套接字作为这些调用的输入。
    • 没有。它仅用于新的连接尝试。
    【解决方案2】:

    积压的作用是提供一个队列,其中包含正在尝试连接到服务器但您尚未处理的客户端。

    这涉及客户端实际连接到服务器和您AcceptEndAccept 客户端之间的时间。

    如果接受客户端需要很长时间,则可能会积压已满,新的客户端连接将被拒绝,直到您有时间处理队列中的客户端。

    关于您的问题:

    1. 我没有这方面的信息。如果默认编号没有造成任何问题(没有拒绝的客户端连接),请将其保留为默认值。如果您在新客户端想要连接时看到许多错误,请增加数量。但是,这可能是因为您花太多时间接受新客户。您应该在增加积压之前解决该问题;

    2. 不,这是由系统处理的。接受客户的正常机制会处理这个问题;

    3. 参见我之前的解释。

    【讨论】:

    • 请问,这是否意味着在积压队列中排队的连接与服务器可以接受的最大连接数无关?例如,积压为 10 的服务器仍然可以容纳 100 甚至 1000 个客户端。谢谢。
    【解决方案3】:

    试试这个程序,你会看到积压有什么好处。

    using System;
    using System.Net;
    using System.Net.Sockets;
    
    /*
       This program creates TCP server socket. Then a large number of clients tries to connect it.
       Server counts connected clients. The number of successfully connected clients depends on the BACKLOG_SIZE parameter.
     */
    
    
    namespace BacklogTest
    {
        class Program
        {
            private const int BACKLOG_SIZE = 0; //<<< Change this to 10, 20 ... 100 and see what happens!!!!
            private const int PORT = 12345;
            private const int maxClients = 100;
    
            private static Socket serverSocket;
            private static int clientCounter = 0;
    
            private static void AcceptCallback(IAsyncResult ar)
            {
                // Get the socket that handles the client request
                Socket listener = (Socket) ar.AsyncState;
                listener.EndAccept(ar);
                ++clientCounter;
                Console.WriteLine("Connected clients count: " + clientCounter.ToString() + " of " + maxClients.ToString());
    
                // do other some work
                for (int i = 0; i < 100000; ++i)
                {
                }
    
                listener.BeginAccept(AcceptCallback, listener);
            }
    
            private static void StartServer()
            {
                // Establish the locel endpoint for the socket
                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, PORT);
    
                // Create a TCP/IP socket
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                // Bind the socket to the local endpoint and listen
                serverSocket.Bind(localEndPoint);
                serverSocket.Listen(BACKLOG_SIZE);
                serverSocket.BeginAccept(AcceptCallback, serverSocket);
            }
    
            static void Main(string[] args)
            {
                StartServer();
    
                // Clients connect to the server.
                for (int i = 0; i < 100; ++i)
                {
                    IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
                    IPEndPoint remoteEP = new IPEndPoint(ipAddress, PORT);
    
                    // Create a TCP/IP socket and connect to the server
                    Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    client.BeginConnect(remoteEP, null, null);
                }
    
                Console.ReadKey();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-06
      • 1970-01-01
      • 2011-02-21
      相关资源
      最近更新 更多