关于套接字的监听和积压的问题

时间:2010-11-23 06:49:09

标签: c# sockets server-side

我正在用C#编写一个需要处理传入连接的应用程序,之前我从未完成过服务器端编程。这引出了以下几个问题:

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

提前致谢。

3 个答案:

答案 0 :(得分:19)

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

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

至于优点和缺点,优点是你可以更好地处理并发连接尝试的高峰,相应的con是操作系统需要为listen backlog队列分配更多的空间,因为它更大。所以这是一场表演与资源的交易。

就我个人而言,我可以通过配置文件进行外部调整。>

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

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

答案 1 :(得分:3)

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

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

如果接受客户端需要很长时间,那么积压可能会变满并且新的客户端连接将被拒绝,直到您有时间从队列中处理客户端为止。

关于你的问题:

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

  2. 不,这是由系统处理的。接受客户的正常机制负责这一点;

  3. 请参阅我之前的解释。

答案 2 :(得分:2)

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

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();
        }
    }
}