Linux OS中的TCP close()vs shutdown()

时间:2018-01-11 13:27:58

标签: linux networking network-programming

我知道stackoverflow中已经有很多类似的问题,但似乎没什么可说服的。基本上试图了解在什么情况下我需要使用一个而不是两个。 还想了解close()& shut_rdwr的shutdown()是一样的。

3 个答案:

答案 0 :(得分:1)

TCP套接字是双向的 - 您通过一个套接字发送和接收。 close()停止双向通信。 shutdown()提供了另一个参数,允许您指定可能要停止使用的方向。

另一个区别(在close()和shutdown(rw)之间)是close()将在另一个进程正在使用它时保持套接字打开,而shutdown()关闭套接字而不管其他进程。

shutdown()通常被客户端用来提供框架 - 表示他们的请求结束,例如一个echo服务可以缓冲它收到的内容,直到客户端shutdown()是它们的发送方,它告诉服务器客户端已经完成,然后服务器回复;客户端可以收到回复,因为它只有shutdown()写入,而不是通过其套接字读取。

答案 1 :(得分:1)

TCP连接的关闭引起了极大的混乱,我们可以说TCP的这一方面设计不当,或者文档中缺少此方面。

简短答案

要正确执行此操作,应依次使用所有3:shutdown(SHUT_WR)shutdown(SHUT_RD)close()。不,shutdown(SHUT_RDWR)close()不同。仔细阅读他们的文档以及关于SO及其相关文章的问题,您需要阅读其中的更多内容以获得概述。

更长的答案

首先要弄清的是目标是。大概您将TCP用于更高级别的协议(请求响应,稳定的数据流等)。一旦您决定“关闭”(终止)连接,就必须发送/接收,发送和接收所有内容(否则,您将不会决定终止)-那么您还想要什么?我正在尝试概述终止时您想要的东西:

  1. 要知道沿任一方向发送的所有数据已到达对等方
  2. 是否存在任何错误(在决定终止时发送数据的过程中,以及在此之后的 以及执行终止本身-这也需要发送/接收数据),通知应用程序
  3. (可选),某些应用程序希望不阻塞,直到终止(包括终止)

不幸的是,TCP不能轻松提供这些功能,并且用户需要了解底层功能以及系统调用与底层功能的交互方式。关键句子在recv联机帮助页中:

   When a stream socket peer has performed an orderly shutdown, the
   return value will be 0 (the traditional "end-of-file" return).

此联机帮助页的含义是,有序关闭是通过一端( A )选择调用shutdown(SHUT_WR)来完成的,这将导致FIN数据包发送到对等方( B ),并且该数据包采用 B 内部recv的返回码为0的形式。 (请注意:联机帮助页未提及FIN数据包,这是实现方面的内容)。联机帮助页将其称为“ EOF”,表示从A到B不再有传输,但是应用程序 B 可以并且应该继续发送其中的内容发送过程,甚至可能发送更多( A 仍在接收)的过程。发送完成后(不久), B 应该自己调用shutdown(SHUT_WR)来关闭双工的另一半。现在,应用 A 收到EOF,并且所有传输已停止。这两个应用程序可以调用shutdown(SHUT_RD)来关闭其套接字以进行读取,然后调用close()来释放与该套接字相关的系统资源(TODO,我还没有找到清晰的文档,据说这2次调用shutdown( SHUT_RD)正在按终止顺序FIN-> ACK,FIN-> ACK发送ACK,但这似乎合乎逻辑)。

对于我们的目标,对于(1)和(2),基本上,应用程序必须以某种方式等待来执行关闭序列,并观察其结果。请注意,如果我们遵循上述小型协议,那么对于两个应用来说,显然终止发起程序( A )已将所有内容发送到了 B 。这是因为 B 收到了EOF(并且只有在其他所有内容之后才收到EOF)。 A 还收到了EOF,该EOF是为响应其自身的EOF而发出的,因此 A 知道 B 收到了所有款项(此处有一个警告-终止协议必须约定谁发起终止-并非两个对等方都立即这样做。但是,事实并非如此。在 B 调用shutdown(SHUT_WR)之后,没有任何返回应用程序级别的消息,告诉 B A 接收了所有发送的数据,以及FIN(A-> B传输已停止!)。如果我错了,请纠正我,但是我相信在这个阶段 B 处于状态“ LAST_ACK”,并且当最终的ACK到达时(4向握手的第4步),得出结论,但是除非已将SO_LINGER设置为足够长的超时时间,否则不会通知应用程序。 SO_LINGER“ ON”指示关闭调用进行阻止(在前台执行),因此关闭调用本身将进行等待。

最后,我建议将SO_LINGER ON配置为较长的​​超时时间,这会导致SO_LINGER阻塞并因此返回任何错误。尚不完全清楚是由于期望LAST_ACK阻止了shutdown(SHUT_WR)还是shutdown(SHUT_RD),但这并不重要,因为我们需要同时调用两者。

对于上述要求#3,例如您具有服务所有连接的单线程设计。使用SO_LINGER可能会在其中之一终止时阻止所有连接。我看到3条解决此问题的路线:

  1. 从另一个线程关闭LINGER。这当然会使设计复杂化
  2. 在后台徘徊,

2A。将FIN和FIN2“提升”为应用级消息,您可以阅读这些消息,然后等待。这基本上将TCP本来要解决的问题提高了一个级别,我认为这有点像hack-ish,也是因为随后的关闭调用可能仍然陷入困境。

2B。尝试找到诸如SIOCOUTQ ioctl之类的here之类的较低级别的工具来查询网络堆栈中未确认的字节数。注意事项很多,这是特定于Linux的,我们不确定它是否适用于FIN ACK(以了解关闭是否已完全完成),此外您还需要定期轮询,这很复杂。总体而言,我倾向于选择1。

我试图撰写有关该问题的综合摘要,欢迎更正/添加。

答案 2 :(得分:-1)

关闭将关闭套接字的发送和接收端。如果您只想发送部分套接字应该关闭不接收部分或反之亦然您可以使用shutdown。

close()------->will close both sending and receiving end.
shutdown()------->only want to close sending or receiving.
  argument:SHUT_RD(shutdown reading end (receiving end))
           SHUT_WR(shutdown writing end(sending end))
SHUT_RDWR(shutdown both)