套接字:连接/接受如何使用 3 次握手(如果使用过)?

moo*_*oog 1 sockets

我有一个 TCP 服务器和多个客户端几乎同时尝试连接到该服务器。我注意到:

\n\n
    \n
  1. 在客户端,connect即使3次握手尚未完成,也可能返回0。

  2. \n
  3. 在服务器端,accept即使完成3次握手也可能不会返回。

  4. \n
\n\n

为了说明这两点,下面是 Wireshark 跟踪(服务器正在侦听端口 1234):

\n\n

1. 以下是客户端返回 0 的情况的 Wireshark 跟踪,connect即使 3 次握手尚未完成(缺少来自客户端的最后一个 SYN):

\n\n
// calls ::connect ...\n59507 \xe2\x86\x92 1234 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1\n1234 \xe2\x86\x92 59507 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=256 SACK_PERM=1\n// ... and ::connect returned 0, despite the above 2 lines \n// forming an incomplete handshake. Why?\n\n// after the ::connect, client calls ::send to send 8 bytes ...\n59507 \xe2\x86\x92 1234 [PSH, ACK] Seq=1 Ack=1 Win=262656 Len=8\n\n// ... but we got reset by peer\n1234 \xe2\x86\x92 59507 [RST] Seq=1 Win=0 Len=0\n\n// ... and as expected, ::send returned 10054 (WSAECONNRESET)\n
Run Code Online (Sandbox Code Playgroud)\n\n

accept2.即使3次握手完成,服务器也没有返回:

\n\n
// server calls ::accept ...\n59643 \xe2\x86\x92 1234 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1\n1234 \xe2\x86\x92 59643 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=256 SACK_PERM=1\n59643 \xe2\x86\x92 1234 [ACK] Seq=1 Ack=1 Win=262656 Len=0\n// At this point, we have a complete handshake, but server's ::accept doesn't return. Why?\n\n// In the next line, server sends a RST. This doesn't cause the server's ::accept call \n// to return an INVALID_SOCKET, which doesn't seem unreasonable (since nothing is accepted), \n// nor is this the point of the question, but I am including the RST trace here \n// for completeness. The main point is that it's as if server's ::accept is oblivious \n// to the successful handshake.\n1234 \xe2\x86\x92 59643 [RST] Seq=1 Win=0 Len=0\n
Run Code Online (Sandbox Code Playgroud)\n\n

问题:

\n\n
    \n
  1. 到底什么时候connect回来?显然,上面的痕迹表明connect即使握手不完整,也可以返回(并声明成功,返回值为 0)。

  2. \n
  3. 到底什么时候accept回来?上面的痕迹表明,完成 3 次握手还不足以accept返回,这令人费解。

  4. \n
\n\n

那么,就好像connect不关心检查握手,而accept严格到成功的握手还不足以让它返回?

\n

m0h*_*ddy 6

所以要回答这个问题,

  • connect()和何时accept()返回?

在此输入图像描述

我引用了 W. Richard Stevens 所著的《Unix Network Planning》来源。connect()握手的前两步后返回。当将accept()条目放入completed queue. 队列的含义如下:

当客户端请求 TCP 连接时,服务器的 TCP 堆栈在 中创建一个条目incomplete queue,然后 3 次 TCP 握手由服务器 TCP 堆栈管理。当 TCP 握手的最后一条消息到达(ACK 或 PiggyBacked ACK)时,incomplete queue连接将从 移动到。completed queue

对于评论中提出的其他问题:

  • backlog(的第二个参数)如何listen()影响队列大小?

backlog不等于队列大小,不同backlog系统设置的队列大小不同。下表列出了backlog某些系统的队列值和相应的队列值:

在此输入图像描述

  • 当服务器收到 SYN 并且队列已满时会做什么?

如果客户端 SYN 到达时队列已满,则 TCP 会忽略到达的 SYN(第 930-931 页 0f TCPv2);它不发送 RST。因为该条件被认为是暂时的,并且客户端可能会在一段时间后重试,希望找到队列中的位置。如果改为发送 RST,则服务器队列是否已满或服务器是否未在该端口上侦听是不明确的。

  • Windows 确实发送 RST。史蒂文斯描述的是 BSD 行为,这更有意义。 (2认同)