关闭vs关闭套接字?

在C语言中,我明白如果我们关闭一个套接字,这意味着套接字将被销毁,以后可以重新使用。

关闭怎么样?描述说它会关闭到该套接字的双工连接的一半。但是该套接字会像close系统调用一样被销毁吗?

292924 次浏览

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

使用shutdown,你仍然能够接收peer已经发送的未决数据(感谢Joey Adams注意到这一点)。

这可能是特定于平台的,我有点怀疑,但无论如何,我所见过的最好的解释是在这个MSDN页面,其中解释了关机,逗留选项,套接字关闭和一般连接终止序列。

总之,使用shutdown在TCP级别发送关闭序列,使用close释放进程中套接字数据结构所使用的资源。如果在调用close时还没有发出显式的关闭序列,则会为您启动一个。

如果使用shutdown(),则可以避免close()的一些限制。

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

close()减少描述符引用计数(维护在文件表项中,计数当前打开的引用文件/套接字的描述符数量),如果描述符不为0,则不关闭套接字/文件。这意味着如果您正在进行fork,则只有在引用计数降为0之后才会进行清理。使用shutdown()可以启动正常的TCP关闭序列,忽略引用计数。

参数说明如下:

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

int how可以是:

SHUT_RD0 不允许进一步接收

SHUT_WR1 不允许进一步发送

SHUT_RDWR2 不允许进一步发送和接收

我也曾在linux下成功地从一个pthread中使用shutdown()迫使另一个当前阻塞在connect()中的pthread提前中止。

在其他操作系统(至少是OSX)下,我发现调用close()足以获得connect()失败。

现有的答案都没有告诉人们shutdownclose是如何在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. 如果可能的话,在close之前考虑shutdown
  2. 如果在决定关闭之前完成了接收(接收到的0大小数据),则在最后一次发送(如果有)结束后关闭连接。
  3. 如果您想正常地关闭连接,请关闭连接(使用SHUT_WR,如果在此之后不关心接收数据,也可以使用SHUT_RD),并等待直到接收到0大小的数据,然后关闭套接字。
  4. 在任何情况下,如果发生任何其他错误(例如超时),只需关闭套接字。

SHUT_RD和SHUT_WR的理想实现

以下内容未经测试,请自行承担风险。然而,我相信这是一种合理和实际的做事方式。

如果TCP堆栈只接收到SHUT_RD关闭,它应该将此连接标记为预期没有更多数据。任何挂起的和后续的read请求(不管它们在哪个线程中)将返回零大小的结果。但是,连接仍然是活动的和可用的——例如,您仍然可以接收OOB数据。此外,操作系统将丢弃它为这个连接接收到的任何数据。但仅此而已,没有包裹会寄到另一边。

如果TCP堆栈只接收到SHUT_WR关闭,它将标记此连接为不能再发送数据。所有挂起的写请求将被完成,但后续的写请求将失败。此外,一个FIN包将被发送到另一边,告诉他们我们没有更多的数据要发送。

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

在我的测试中。

当socket未与其他进程共享时,close将立即发送fin数据包并销毁fd

shutdown SHUT_RD,进程仍然可以从套接字接收数据,但如果TCP缓冲区为空,recv将返回0。当对等体发送更多数据后,recv将再次返回数据。

shutdown SHUT_WR将发送fin包,以指示进一步的发送是不允许的。对等体可以recv数据,但如果它的TCP缓冲区是空的,它将recv 0

如果对等端发送更多数据,shutdown SHUT_RDWR(等于同时使用SHUT_RDSHUT_WR)将发送第一个数据包。

关闭

当你用完了一个套接字,你可以简单地用close关闭它的文件描述符;如果仍有数据等待通过连接传输,通常close会尝试完成此传输。您可以使用SO_LINGER套接字选项来指定超时时间来控制这种行为;参见套接字选项。

关闭

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

shutdown命令用来关闭socket的连接。它的参数how指定执行什么动作: 0 停止接收此套接字的数据。如果进一步的数据到达,拒绝它。 1 停止尝试从这个套接字传输数据。丢弃任何等待发送的数据。停止寻找已发送数据的确认;如果它丢失了,不要重新发送。 2 停止接收和传输。< / p >

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

Linux: shutdown()导致侦听器线程select()唤醒并产生错误。关闭();close ();会导致无尽的等待。

Winsock:反之亦然- shutdown()没有作用,而close()被成功捕获。