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

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

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

提前致谢。

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

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

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

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

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

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

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

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

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

关于你的问题:

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

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

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