什么是TCP连接中的“待办事项”?

时间:2016-04-13 09:38:08

标签: python sockets tcp network-programming udp

下面,您将看到一个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)。

基于互联网上的教程:

  

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

可是:

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

3 个答案:

答案 0 :(得分:26)

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

  

这些排队的连接是什么?

简单来说, BACKLOG 等于队列将保留的待处理连接数。

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

  

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

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

  

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

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

  

首选哪个值?

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

  

这个积压是仅针对TCP连接定义的,还是我们将其用于UDP   和其他协议呢?

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

但是,请记住,然后有TCP based datagram socket implementations(称为TCPDatagramSocket)也有积压参数。

答案 1 :(得分:17)

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

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

所以积压限制是关于未建立的未决连接。

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

答案 2 :(得分:-1)

该参数的功能似乎是要限制服务器将保留在队列中的传入连接请求的数量,前提是该服务器可以在合理的时间内(虽然处于高负载状态下)处理当前请求和少量排队的未决请求加载。这是我反对的一个很好的段落,它为该论点提供了一些语境...

  

最后,listen参数告诉套接字库我们想要   最多可以排队5个连接请求(正常最大)   拒绝外部联系。如果其余代码被编写   正确,应该足够。

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

文档中较早的文本建议客户端应该插入和退出服务器,这样一来您就不会建立很长的请求队列...

  

connect完成后,套接字s可用于发送   请求页面文本。相同的套接字将读取回复,   然后被摧毁。是的,毁了。客户端套接字是   通常仅用于一次交换(或一小部分顺序   交流)。

要快速了解有关套接字的网络编程信息,必须阅读链接的HowTo指南。它确实使一些与此有关的大主题成为焦点。现在,服务器套接字如何根据实现细节来管理此队列是另一回事了,可能很有趣。我认为这种设计的动机更能说明问题,没有它,施加denial of service attack的障碍将非常低。

关于最小值为0对1的原因,我们应该记住0仍然是一个有效值,意味着没有排队。从本质上讲,就是说不存在请求队列,如果服务器套接字当前正在为连接提供服务,则直接拒绝连接。在这种情况下,应始终牢记当前处于活动状态的连接的要点,这是队列首先引起关注的唯一原因。

这使我们进入有关首选值的下一个问题。这都是设计决定,您是否要排队请求?如果是这样,您可以选择一个值,那么您会认为根据预期的流量和我认为的已知硬件资源是有保证的。我怀疑选择值是否有任何公式化。这让我想知道,首先要处理请求的轻量级如何使您在服务器上排队的一切都要受到惩罚。


更新

我想证实user207421的注释,然后去查找python源。不幸的是,在散列530f506的sockets.py源中找不到这种详细程度,而在socketmodule.c#L3351-L3382中却找到了。

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

  

我们尝试选择一个足够高的默认积压以避免连接   可以减少常见工作负载,但又不会过高而限制资源使用。

  

如果指定了积压,则必须至少为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.hsocket.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 参数,我们必须意识到对于给定   在侦听套接字时,内核维护两个队列:

     

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

     

已完成的连接队列,其中   包含每个与TCP三向访问的客户端的条目   握手已完成。这些套接字处于ESTABLISHED状态   (图2.4)。下图描述了这两个队列:

     

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