题 关闭vs关机插座?


在C中,我理解如果我们关闭套接字,则意味着套接字将被销毁并且可以在以后重新使用。

关机怎么样?描述说它关闭了与该套接字的双工连接的一半。但是那个插座会被破坏吗? close 系统调用?


169
2017-11-11 23:38


起源




答案:


这是 解释 在Beej的网络指南中。 shutdown 是一种在一个或两个方向上阻止通信的灵活方式。当第二个参数是 SHUT_RDWR,它会阻止发送和接收(如 close)。然而, close 是实际销毁套接字的方法。

shutdown,你仍然可以收到同伴已发送的待处理数据(感谢Joey Adams注意到这一点)。


148
2017-11-11 23:39



请记住,即使你关闭()TCP套接字,它也不一定会立即重复使用,因为它将处于TIME_WAIT状态,而操作系统确保没有未完成的数据包可能会被混淆为新信息,如果你是立即重用该套接字的其他东西。 - alesplin
套接字关闭和关闭之间的巨大差异是套接字被其他进程共享时的行为。 shutdown()会影响 所有 close()时套接字的副本仅影响一个进程中的文件描述符。 - Zan Lynx
关于TIME_WAIT的评论不正确。这适用于端口,而不适用于套接字。你不能重用套接字。 - user207421
-1。这篇文章和链接都省略了一个想要使用的重要概念原因 shutdown:向对等方发送EOF信号,仍然能够接收对等方发送的待处理数据。 - Joey Adams
@MatthewFlaschen:这仅适用于您使用的情况 shutdown(SD_SEND)作为 shutdown(SD_BOTH) 会导致随后的 recv失败了(虽然快速测试显示了一些 recv即使在接收关闭后,s也可能成功,因为字节是缓冲的)。我将downvote更改为upvote。 - Joey Adams


现有的答案都没有告诉人们如何 shutdown 和 close 适用于TCP协议级别,因此值得添加此功能。

标准TCP连接通过4路终结终止:

  1. 一旦参与者没有更多数据要发送,它就会向另一个发送FIN数据包
  2. 另一方返回FIN的ACK。
  3. 当另一方也完成数据传输时,它会发送另一个FIN数据包
  4. 初始参与者返回ACK并完成传输。

但是,还有另一种“紧急”方式来关闭TCP连接:

  1. 参与者发送RST数据包并放弃连接
  2. 另一方收到一个RST,然后放弃连接

在我使用Wireshark的测试中,使用默认套接字选项, shutdown 发送一个FIN数据包到另一端,但它就是这样。在另一方向您发送FIN数据包之前,您仍然可以接收数据。一旦发生这种情况,你的 Receive 将获得0大小的结果。因此,如果您是第一个关闭“发送”的人,则应在完成数据接收后关闭套接字。

另一方面,如果你打电话 close 虽然连接仍处于活动状态(另一端仍然处于活动状态,并且您可能在系统缓冲区中也有未发送的数据),RST数据包将被发送到另一端。这有利于错误。例如,如果您认为对方提供了错误的数据或拒绝提供数据(DOS攻击?),您可以立即关闭套接字。

我对规则的看法是:

  1. 考虑 shutdown 之前 close 如果可能
  2. 如果在决定关闭之前完成了接收(接收的0大小数据),请在最后一次发送(如果有)完成后关闭连接。
  3. 如果要正常关闭连接,请关闭连接(使用SHUT_WR,如果您在此之后不关心接收数据,也使用SHUT_RD),并等待直到收到0大小的数据,然后关闭插座。
  4. 在任何情况下,如果发生任何其他错误(例如超时),只需关闭套接字即可。

SHUT_RD和SHUT_WR的理想实现

以下未经测试,信任风险自负。但是,我相信这是一种合理而实用的做事方式。

如果TCP堆栈仅通过SHUT_RD接收关闭,则应将此连接标记为不再需要数据。任何待定和随后的 read 请求(无论它们处于哪个线程)将返回零大小的结果。但是,连接仍然是活动的和可用的 - 例如,您仍然可以接收OOB数据。此外,操作系统将删除它为此连接接收的任何数据。但就是这样,没有包裹会被发送到另一方。

如果TCP堆栈仅通过SHUT_WR收到关闭,则应标记此连接,因为不能再发送数据。所有挂起的写入请求都将完成,但后续写入请求将失败。此外,FIN数据包将被发送到另一侧以通知他们我们没有更多数据要发送。


94
2018-05-05 23:19



“如果在连接仍处于活动状态时呼叫关闭”是同义反复的,并且它不会导致发送RST。 (1)没有必要。在(4)中,超时不一定对连接是致命的,并且不是不变的,表明你可以关闭它。 - user207421
@EJP不,这不是同义反复。您可以 shutdown() 连接然后它不再活着。你仍然有文件描述符。你还可以 recv() 来自接收缓冲区。你还需要打电话 close() 处置文件描述符。 - Pavel Šimerda
这是有关电线格式的最佳答案。我对有关使用SHUT_RD关闭的更多细节感兴趣。没有预期更多数据的TCP信令,对吗?是不是只有FIN发信号通知不发送更多数据? - Pavel Šimerda
@PavelŠimerda是的,TCP并不表示不期待更多数据。这应该在高级协议中考虑。在我看来,这通常不是必需品。你可以关上门,但你无法阻止人们把礼物放在门前。这是他们的决定,而不是你的决定。 - Earth Engine
@EJP你有任何实际的论点吗?否则我不感兴趣。 - Pavel Šimerda


有一些限制 close() 如果使用,可以避免 shutdown() 代替。

close() 将终止TCP连接上的两个方向。有时您想告诉另一个端点您已完成发送数据,但仍希望接收数据。

close() 递减描述符引用计数(在文件表条目中维护并计算当前打开的引用文件/套接字的描述符的数量),如果描述符不为0则不关闭套接字/文件。这意味着如果要分配,只有在引用计数降为0后才会进行清理 shutdown() 一个人可以启动正常的TCP关闭序列,忽略引用计数。

参数如下:

int shutdown(int s, int how); // s is socket descriptor

int how 可:

SHUT_RD 要么 0     不允许进一步接收

SHUT_WR 要么 1     不允许进一步发送

SHUT_RDWR 要么 2     不允许进一步发送和接收


29
2017-11-12 09:05



这两个功能用于完全不同的目的。如果尚未启动一个套接字上的最后关闭启动关闭序列这一事实不会改变close用于清理套接字数据结构的事实,而关闭则用于启动tcp级别关闭序列。 - Len Holgate
你不能'使用shutdown而不是'。你可以使用它 同样。 但你必须关闭插座一段时间。 - user207421


这可能是平台特定的,我不知何故怀疑它,但无论如何,我见过的最好的解释是 在这个msdn页面上 他们解释了关闭,延迟选项,套接字关闭和一般连接终止序列。

总之,使用shutdown在TCP级别发送关闭序列,并使用close来释放进程中套接字数据结构使用的资源。如果在调用close时尚未发出明确的关闭序列,则会启动一个关闭序列。


14
2017-11-12 09:04





我在linux下也取得了成功 shutdown() 从一个pthread强制另一个pthread目前阻止 connect() 早点流产

在其他操作系统(至少OSX)下,我发现了呼叫 close() 足以得到 connect() 失败。


6
2017-11-15 08:56





“shutdown()实际上并没有关闭文件描述符 - 它只是改变了它的可用性。要释放套接字描述符,你需要使用close()。”1 


6
2017-11-12 01:22





使用完套接字后,只需关闭其文件描述符即可;如果仍有数据等待通过连接传输,则通常关闭会尝试完成此传输。您可以使用SO_LINGER套接字选项控制此行为以指定超时期限;请参阅套接字选项。

关掉

您也可以通过调用shutdown来关闭连接上的接收或传输。

关闭功能会关闭套接字的连接。它的参数如何指定要执行的操作: 0 停止接收此套接字的数据。如果有更多数据到达,请拒绝。 1 停止尝试从此套接字传输数据。丢弃等待发送的任何数据。停止寻找已发送数据的确认;如果丢失则不要重新发送。 2 停止接收和传输。

成功时返回值为0,失败时返回-1。


1
2017-07-07 07:39