【发布时间】:2011-06-29 20:16:50
【问题描述】:
我试图了解 BSD 套接字接口中的事件如何转换为 TCP 连接的状态。特别是,我试图了解连接过程的哪个阶段accept() 在服务器端返回
- 客户端发送SYN
- 服务器发送 SYN+ACK
- 客户端发送 ACK
accept() 会在哪一个步骤中返回?
【问题讨论】:
标签: linux networking tcp
我试图了解 BSD 套接字接口中的事件如何转换为 TCP 连接的状态。特别是,我试图了解连接过程的哪个阶段accept() 在服务器端返回
accept() 会在哪一个步骤中返回?
【问题讨论】:
标签: linux networking tcp
accept 在连接完成时返回。 在客户端发送他的 ACK 后连接完成。
accept 为您提供了一个可以通信的套接字。当然你知道,在建立连接之前你不能通信。并且在握手之前无法建立连接。
在客户端感知到他的 ACK 之前返回是没有意义的。在最初的 SYN 之后,他完全有可能不会说任何话。
【讨论】:
内核中的 TCP/IP 堆栈代码通常[1] 完全完成三次握手,无需任何用户空间代码的干预。您列出的三个步骤都发生在accept() 返回之前之前。事实上,它们可能发生在 accept() 甚至被调用之前!
当你告诉堆栈listen() 用于特定 TCP 端口上的连接时,你传递了一个 backlog 参数,它告诉内核它可以代表你的程序一次静默接受多少个连接。当内核自动接受新的连接请求时,正是这个队列被使用,并且它们被保留在那里直到你的程序得到accept()ing它们。当您调用accept() 时,当listen backlog 队列中有一个或多个连接时,所发生的一切就是将最旧的连接从队列中删除并绑定到一个新的套接字。[2]
换句话说,如果你的程序调用listen(sd, 5),然后进入一个无限的无操作循环,因此它永远不会调用accept(),从客户端的角度来看,五个并发的客户端连接请求将成功。第六个连接请求将在第一个 SYN 数据包上停止,直到拥有 TCP 端口的程序调用 accept() 或其他客户端之一断开其连接。
[1] 当然,防火墙和其他堆栈修改可以改变这种行为。我在这里只说默认的 BSD 套接字堆栈行为。
[2] 如果调用accept()时没有等待在backlog中的连接,则默认阻塞,除非监听套接字设置为非阻塞,在这种情况下它返回-1并且errno是EWOULDBLOCK.
【讨论】: