什么是TCP连接中的"积压"?

25 python sockets udp network-programming tcp

下面,您将看到一个python程序,它充当侦听端口9999的连接请求的服务器:

# server.py 
import socket                                         
import time

# create a socket object
serversocket = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM) 

# get local machine name
host = socket.gethostname()                           

port = 9999                                           

# bind to the port
serversocket.bind((host, port))                                  

# queue up to 5 requests
serversocket.listen(5)                                           

while True:
    # establish a connection
    clientsocket,addr = serversocket.accept()      

    print("Got a connection from %s" % str(addr))
    currentTime = time.ctime(time.time()) + "\r\n"
    clientsocket.send(currentTime.encode('ascii'))
    clientsocket.close()
Run Code Online (Sandbox Code Playgroud)

问题是socket.listen()方法参数(即5)的功能是什么.

基于互联网上的教程:

backlog参数指定排队连接的最大数量,并且应该至少为0; 最大值取决于系统(通常为5),最小值强制为0.

但:

  1. 这些排队连接是什么?
  2. 是否对客户请求进行了任何更改?(我的意思是运行socket.listen(5)的服务器与socket.listen(1)接受连接请求或接收数据时运行的服务器不同?)
  3. 为什么最小值为零?不应该至少是这样1吗?
  4. 哪个值更受欢迎?
  5. 这是backlog仅为TCP连接定义的,还是我们为UDP和其他协议定义的?

Am_*_*ful 35

注意:答案的框架没有任何Python背景,但问题与语言无关,需要回答.

这些排队的连接是什么?

简单来说,BACKLOG等于队列将保留的挂起连接数.

当多个客户端连接到服务器时,服务器然后将传入的请求保存在队列中.客户端被安排在队列中,并且当队列成员继续时,服务器逐个处理它们的请求.这种连接的本质称为排队连接.

是否对客户请求进行了任何更改?(我的意思是与socket.listen(5)一起运行的服务器与在接受连接请求或接收数据时使用socket.listen(1)运行的服务器不同?)

是的,两种情况都不同.第一种情况只允许将5个客户端安排到队列中; 而在backlog = 1的情况下,队列中只能保留1个连接,从而导致进一步连接请求丢失!

为什么最小值为零?它不应该至少1?

我不知道Python,但是,根据这个源,在C中,backlog参数为0可能允许套接字接受连接,在这种情况下,监听队列的长度可以设置为实现定义的最小值.

哪个值更受欢迎?

这个问题没有明确的答案.我会说这取决于应用程序的性质,以及硬件配置和软件配置.同样,根据来源,socket.listen(5)静默地限制在1到5之间(包括C).

这个积压是仅为TCP连接定义的,还是我们为UDP和其他协议定义的?

没有.请注意,对于未连接的数据报套接字(UDP),不需要listen()或accept().这是使用未连接的数据报套接字的好处之一!

但是,请记住,然后有socket.listen(1)(称为TCPDatagramSocket)也有backlog参数.


fre*_*ish 19

当建立TCP连接时,执行所谓的三次握手.双方交换一些数据包,一旦这样做,这个连接被称为完成,它已准备好供应用程序使用.

然而,这种三次握手需要一些时间.在此期间,连接排队,这是积压.因此,您可以通过.listen(no)调用设置不完整并行连接的最大数量(请注意,根据posix标准,该值只是一个提示,可能完全被忽略).如果有人试图在积压限制之上建立连接,则另一方将拒绝它.

因此,积压限制是关于未建立的挂起连接.

现在,在大多数情况下,更高的积压限制会更好.请注意,最大限制取决于操作系统,例如cat /proc/sys/net/core/somaxconn,128在我的Ubuntu上提供.

  • 如果服务器是Windows,则服务器将拒绝它。Unix,Linux等上的TCP仅丢弃SYN,这可能导致连接方重试,并且如果条件仍然存在,将导致连接*超时*,而不是拒绝。 (2认同)
  • 它不是不完整连接的数量。他们在不同的队列中。它是应用程序尚未接受的 *已完成 * 连接数。答案完全不正确。请参阅接受的答案以了解真相。 (2认同)

jxr*_*mos 5

该参数的功能似乎是限制服务器在队列中保留的传入连接请求的数量,假设服务器可以在高负载下的合理时间内为当前请求和少量排队的待处理请求提供服务。这是我反对的一个很好的段落,它为这个论点提供了一些背景......

\n\n
\n

最后,listen 的参数告诉套接字库,我们希望它在拒绝外部连接之前将多达 5 个连接请求(正常的最大值)排队。如果其余代码编写得正确,那就足够了。

\n
\n\n

https://docs.python.org/3/howto/sockets.html#creating-a-socket

\n\n

文档中前面的文本建议客户端应该进出服务器,这样您就不会首先建立一个很长的请求队列......

\n\n
\n

完成后connect,可以使用套接字s发送\n 对页面文本的请求。相同的套接字将读取回复,然后被销毁。那\xe2\x80\x99是对的,被毁了。客户端套接字通常仅用于一次交换(或一小组顺序交换)。

\n
\n\n

在加快使用套接字进行网络编程时,必须阅读链接的 HowTo 指南。它确实使一些有关它的大主题成为焦点。现在,服务器套接字如何管理这个队列的实现细节是另一回事了,可能是一个有趣的故事。我认为这种设计的动机更能说明问题,如果没有它,造成拒绝服务攻击的障碍将会非常非常低。

\n\n

至于最小值0 与 1 的原因,我们应该记住,0 仍然是一个有效值,意味着不排队。这本质上是说,如果服务器套接字当前正在为连接提供服务,则不要有请求队列,只需直接拒绝连接。在这种情况下,应始终牢记当前正在服务的活动连接的要点,这是首先对队列感兴趣的唯一原因。

\n\n

这给我们带来了下一个关于首选值的问题。这都是一个设计决定,你是否想要对请求进行排队?如果是这样,您可以根据我认为的预期流量和已知硬件资源来选择您认为合理的值。我怀疑选择值时是否有任何公式化的东西。这让我想知道一个请求到底有多轻,以至于您在服务器上排队时会面临惩罚。

\n\n
\n\n

更新

\n\n

我想证实 user207421 的评论并去查找 python 源代码。不幸的是,这种级别的细节在sockets.py源中找不到,而是在socketmodule.c#L3351-L3382(哈希值530f506)中找到。

\n\n

这些评论非常有启发性,我将逐字复制下面的源代码,并在此处挑出非常有启发性的澄清评论......

\n\n
\n

我们尝试选择足够高的默认积压,以避免常见工作负载的连接丢失,但又不会太高以限制资源使用。

\n
\n\n

\n\n
\n

如果指定了 backlog,则它必须至少为 0(如果较低,则设置为 0);它指定系统在拒绝新连接之前允许\n 的未接受连接数。如果未指定,则选择默认的合理值。

\n
\n\n
/* s.listen(n) method */\n\nstatic PyObject *\nsock_listen(PySocketSockObject *s, PyObject *args)\n{\n    /* We try to choose a default backlog high enough to avoid connection drops\n     * for common workloads, yet not too high to limit resource usage. */\n    int backlog = Py_MIN(SOMAXCONN, 128);\n    int res;\n\n    if (!PyArg_ParseTuple(args, "|i:listen", &backlog))\n        return NULL;\n\n    Py_BEGIN_ALLOW_THREADS\n    /* To avoid problems on systems that don\'t allow a negative backlog\n     * (which doesn\'t make sense anyway) we force a minimum value of 0. */\n    if (backlog < 0)\n        backlog = 0;\n    res = listen(s->sock_fd, backlog);\n    Py_END_ALLOW_THREADS\n    if (res < 0)\n        return s->errorhandler();\n    Py_RETURN_NONE;\n}\n\nPyDoc_STRVAR(listen_doc,\n"listen([backlog])\\n\\\n\\n\\\nEnable a server to accept connections.  If backlog is specified, it must be\\n\\\nat least 0 (if it is lower, it is set to 0); it specifies the number of\\n\\\nunaccepted connections that the system will allow before refusing new\\n\\\nconnections. If not specified, a default reasonable value is chosen.");\n
Run Code Online (Sandbox Code Playgroud)\n\n

进一步深入外部,我从套接字模块追踪以下来源......

\n\n
 res = listen(s->sock_fd, backlog);\n
Run Code Online (Sandbox Code Playgroud)\n\n

该源代码位于socket.hsocket.c,使用linux 作为讨论目的的具体平台背景。

\n\n
/* Maximum queue length specifiable by listen.  */\n#define SOMAXCONN   128\nextern int __sys_listen(int fd, int backlog);\n
Run Code Online (Sandbox Code Playgroud)\n\n

在手册页中可以找到更多信息

\n\n

http://man7.org/linux/man-pages/man2/listen.2.html

\n\n
int listen(int sockfd, int backlog);\n
Run Code Online (Sandbox Code Playgroud)\n\n

以及相应的文档字符串

\n\n
\n

listen()将套接字标记为sockfd被动套接字,即使用accept(2) 接受传入连接请求的套接字。

\n\n

参数sockfd是一个文件描述符,引用\n 类型SOCK_STREAM或 的套接字SOCK_SEQPACKET

\n\n

backlog参数定义挂起连接的队列sockfd可以增长到的最大长度。如果连接请求在队列已满时到达,则客户端可能会收到一个错误,并指示,ECONNREFUSED或者如果底层协议支持重传,则该请求可能会被忽略,以便稍后重新尝试连接成功了。

\n
\n\n

另一种来源表明内核负责积压队列。

\n\n
\n

该函数的第二个参数backlog指定内核应为此套接字排队的最大连接数。

\n
\n\n

他们继续简要介绍了如何在积压中划分未接受/排队的连接(链接源中包含一个有用的数字)。

\n\n
\n

要理解backlog参数,我们必须认识到对于给定的侦听套接字,内核维护两个队列:

\n\n

完整的连接队列,其中包含从客户端到达的每个 SYN 的条目,服务器正在等待 TCP 三向握手的完成。这些套接字处于\n SYN_RCVD状态(图2.4)。

\n\n

已完成的连接队列,其中包含已完成 TCP 三向握手的每个客户端的条目。这些套接字处于ESTABLISHED\n状态(图2.4)。这两个队列如下图所示:

\n\n

当在不完整队列上创建条目时,来自侦听套接字的参数将被复制到新创建的连接。连接创建机制是完全自动的;不涉及服务器进程。

\n
\n

  • 值得一提的是 `tcp_abort_on_overflow` http://veithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html (2认同)