TCP 连接中的“积压”是啥?

Posted

技术标签:

【中文标题】TCP 连接中的“积压”是啥?【英文标题】:What is "backlog" in TCP connections?TCP 连接中的“积压”是什么? 【发布时间】:2016-08-04 07:30:48 【问题描述】:

在下面,您会看到一个 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()

问题是socket.listen()方法的参数的作用是什么(即5)。

基于互联网上的教程:

积压参数指定排队的最大数量 连接数且应至少为 0;最大值是 系统相关(通常为 5),最小值被强制为 0。

但是:

    这些排队的连接是什么? 它对客户请求有什么影响吗? (我的意思是使用socket.listen(5) 运行的服务器与使用socket.listen(1) 运行的服务器在接受连接请求或接收数据时是否不同?) 为什么最小值为零?不应该是至少1吗? 有首选值吗? backlog 是仅为 TCP 连接定义的,还是也适用于 UDP 和其他协议?

【问题讨论】:

这个answer 解释得比公认的要好得多。 【参考方案1】:

注意:答案是在没有任何 Python 背景的情况下进行的,但是,这些问题与语言无关,需要回答。

这些排队的连接是什么?

简单来说,积压参数指定队列将持有的挂起连接数。

当多个客户端连接到服务器时,服务器会将传入的请求保存在队列中。客户端排列在队列中,服务器在队列成员进行时一一处理它们的请求。这种连接的性质称为排队连接。

它对客户请求有什么影响吗? (我的意思是服务器 以socket.listen(5) 运行的服务器与 在接受连接请求时使用socket.listen(1) 运行或 在接收数据?)

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

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

我不知道 Python,但是,as per this source,在 C 中,积压参数 0 可能允许套接字接受连接,在这种情况下,侦听队列的长度可以设置为实现定义的最小值价值。

有首选值吗?

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

此积压是仅为 TCP 连接定义还是适用于 UDP 和其他协议?

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

但是,请记住,还有基于 TCP 的数据报套接字实现(称为 TCPDatagramSocket)也有积压参数。

【讨论】:

如果服务器是Windows,服务器将拒绝它。 Unix、Linux 等上的 TCP 只是简单的丢掉 SYN,这可能会导致连接端重试,如果这种情况持续存在会导致连接 超时,而不是拒绝。 @EJP - 我同意你的观点。但是,它应该是客户端(连接)端的连接超时,但是,服务器端会丢弃请求(SYN)。请检查您现在是否清楚! 这与我写的没有什么不同,只是你仍然忽略了 Windows 案例。如果您不这么认为,请说明原因。 @user207421 - 请您引用我帖子中的那句话,您认为我将 Windows 机箱留在了哪里?我已经同意你在我之前的评论中提出的观点!另外,当我的意思是连接被丢弃时,它是否也包括被拒绝(不接受)的连接?我想任何人都可以推断出来。【参考方案2】:

当 TCP 连接建立时,执行所谓的三向握手。双方交换一些数据包,一旦完成,这个连接就被称为完成,它可以被应用程序使用。

但是,这种三次握手需要一些时间。在此期间,连接排队,这是积压。所以你可以通过.listen(no)调用来设置不完整并行连接的最大数量(注意根据posix标准the value is only a hint,它可能会被完全忽略)。如果有人试图建立超过 backlog 限制的连接,对方将拒绝它。

所以积压限制是关于挂起的连接,而不是建立的。

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

【讨论】:

如果服务器是Windows,服务器将拒绝它。 Unix、Linux 等上的 TCP 只是简单的丢掉 SYN,这可能会导致连接端重试,如果这种情况持续存在会导致连接 超时,而不是拒绝。 而且不是不完整的连接数。他们在不同的队列中。它是应用程序尚未接受的已完成 连接数。答案是完全错误的。请参阅公认的答案以了解真相。【参考方案3】:

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

最后,listen 的参数告诉套接字库我们想要 它在之前排队多达 5 个连接请求(正常最大值) 拒绝外部联系。如果剩下的代码都写好了 对了,应该够了。

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

文档前面有一段文字建议客户端应该进入和退出服务器,这样你就不会首先建立一个长长的请求队列......

connect 完成后,可以使用套接字s 发送一个 请求页面的文本。同一个套接字会读取回复, 然后被摧毁。没错,毁了。客户端套接字是 通常只用于一次交换(或一小组顺序 交流)。

在加快使用套接字进行网络编程时,必须阅读链接的 HowTo 指南。它确实引起了一些关于它的大图主题的关注。现在,就实现细节而言,服务器套接字如何管理这个队列是另一回事,可能是一个有趣的故事。我想这种设计的动机更能说明问题,没有它,造成denial of service attack 的障碍将非常非常低。

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

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


更新

我想证实来自 user207421 的 cmets 并去查找 python 源。不幸的是,在 sockets.py 源中找不到这种详细程度,而是在哈希 530f506 的 socketmodule.c#L3351-L3382 中找到。

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

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

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

/* s.listen(n) method */

static PyObject *
sock_listen(PySocketSockObject *s, PyObject *args)

    /* We try to choose a default backlog high enough to avoid connection drops
     * for common workloads, yet not too high to limit resource usage. */
    int backlog = Py_MIN(SOMAXCONN, 128);
    int res;

    if (!PyArg_ParseTuple(args, "|i:listen", &backlog))
        return NULL;

    Py_BEGIN_ALLOW_THREADS
    /* To avoid problems on systems that don't allow a negative backlog
     * (which doesn't make sense anyway) we force a minimum value of 0. */
    if (backlog < 0)
        backlog = 0;
    res = listen(s->sock_fd, backlog);
    Py_END_ALLOW_THREADS
    if (res < 0)
        return s->errorhandler();
    Py_RETURN_NONE;


PyDoc_STRVAR(listen_doc,
"listen([backlog])\n\
\n\
Enable a server to accept connections.  If backlog is specified, it must be\n\
at least 0 (if it is lower, it is set to 0); it specifies the number of\n\
unaccepted connections that the system will allow before refusing new\n\
connections. If not specified, a default reasonable value is chosen.");

进一步深入兔子洞进入外部,我从 socketmodule 追踪以下来源...

 res = listen(s->sock_fd, backlog);

此来源在socket.h 和socket.c 结束,使用 linux 作为具体平台背景进行讨论。

/* Maximum queue length specifiable by listen.  */
#define SOMAXCONN   128
extern int __sys_listen(int fd, int backlog);

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

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

int listen(int sockfd, int backlog);

以及对应的文档字符串

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

sockfd 参数是一个文件描述符,它引用一个套接字 输入SOCK_STREAMSOCK_SEQPACKET

backlog 参数定义队列的最大长度 sockfd 的待处理连接数可能会增加。如果一个连接请求 当队列已满时到达,客户端可能会收到错误消息 ECONNREFUSED 的指示,或者,如果底层协议 支持重传,请求可以被忽略,以便稍后 重试连接成功。

另外一个source 将内核标识为负责积压队列。

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

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

要理解 backlog 参数,我们必须意识到对于给定的 监听socket,内核维护两个队列:

不完整的连接队列,其中包含每个 SYN 的条目 来自服务器正在等待的客户端 TCP 三次握手完成。这些插座在 SYN_RCVD 状态(图 2.4)。

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

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

【讨论】:

您的来源不正确。积压问题是针对已完成的连接。连接请求进入不同的队列,并在完成后移至积压队列。 事实上它是一个全面的劣质资源。有几件事是错误的,不仅仅是这一件事。关于每个套接字只使用一次传输的 HTTP 声明是非常不正确的,就像你应该在关闭之前使用 shutdown() 的声明一样 哇,这确实会彻底改变事情。我想知道为什么没有关于该文档的任何内容,它已经在 python 文档中出现了一段时间。它是由 Gordon McMillan 编写的。 @user207421 我刚刚查看了 c 源代码,积压工作似乎围绕传入的未接受请求。 值得一提的是tcp_abort_on_overflowveithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html

以上是关于TCP 连接中的“积压”是啥?的主要内容,如果未能解决你的问题,请参考以下文章

TCP的连接方式是啥?

TCP数据段中的紧急指的是啥

tcp是啥

tcp是啥意思

redis.conf 中的“tcp-backlog”是啥

TCP/IP是啥意思?