网编(6):优雅地断开套接字连接

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/88951558

我们是调用close 或closesocket 函数只是单方面断开连接。

————————————————————————————————————————————————

基于TCP 的半关闭

TCP中的断开连接过程比建立连接过程更重要,因为连接过程中一般不会出现大的变数,但断开过程有可能发生预想不到的情况,因此应准确掌控。只有掌握了下面要讲解的半关闭(Half-close), 才能明确断开过程。

单方面断开连接带来的问题
Linux 的close函数和Windows的closesocket函数意味着完全断开连接。完全断开不仅指无法传输数据,而且也不能接收数据。因此, 在某些情况下,通信一方调用close或closesocket函数断开连接就显得不太优雅,如图所示。

上图描述的是2 台主机正在进行双向通信。主机A发送完最后的数据后,调用close函数断开了连接,之后主机A无法再接收主机B传输的数据。实际上,是完全无法调用与接收数据相关的函数。最终,由主机B传输的、主机A必须接收的数据也销毁了。

为了解决这类问题,“只关闭一部分数据交换中使用的流" ( Half-close ) 的方法应运而生。断开一部分连接是指 “可以传输数据但无法接收,或可以接收数据但无法传输”。顾名思义就是只关闭流的一半。

————————————————————————————————————————————————

套接字和流( Stream )

两台主机通过套接字建立连接后进入可交换数据的状态,又称“流形成的状态"。也就是把建立套接字后可交换数据的状态看作一种流。

此处的流可以比作水流。水朝着一个方向流动,同样,在套接字的流中,数据也只能向一个方向移动。因此,为了进行双向通信,需要如图所示的2个流。

一旦两台主机间建立了套接字连接,每个主机就会拥有单独的输入流和输出流。当然,其中一个主机的输入流与另一主机的输出流相连,而输出流则与另一主机的输入流相连。另外,“优雅地断开连接方式”只断开其中1 个流,而非同时断开两个流。Linux的close和Windows的closesocket函数将同时断开这两个流,因此与“优雅” 二字还有一段距离。

————————————————————————————————————————————————

针对优雅断开的 shutdown 函数

#include <sys/socket.h>
int shutdown(int sock, int howto);
//成功时返回0, 失败时返回-1。

#sock 需要断开的套接字文件描述符。
#howto 传递断开方式信息。

调用上述函数时, 第二个参数决定断开连接的方式,其可能值如下所示。

  • SHUT_RD: 断开输入流,套接字无法接收数据。即使输入缓冲收到数据也会抹去,而且无法调用输入相关函数。
  • SHUT_WR: 中断输出流,也就无法传输数据。但如果输出缓冲还留有未传输的数据,则将传递至目标主机。
  • SHUT RDWR: 同时中断I/O流。这相当于分2次调用shutdown ,其中一次以SHUT_RD为参数,另一次以SHUT_WR为参数。

————————————————————————————————————————————————

为何需要半关闭

close与shutdown的区别主要表现在:
close函数会关闭套接字ID,如果有其他的进程共享着这个套接字,那么它仍然是打开的,这个连接仍然可以用来读和写,并且有时候这是非常重要的 ,特别是对于多进程并发服务器来说。

而shutdown会切断进程共享的套接字的所有连接,不管这个套接字的引用计数是否为零,那些试图读得进程将会接收到EOF标识,那些试图写的进程将会检测到SIGPIPE信号,同时可利用shutdown的第二个参数选择断连的方式。

调用close函数的同时关闭1/0流,这样也会向对方发送EOF 。但此时无法再接收对方传输的数据。换言之,若调用close函数关闭流,就无法接收客户端最后发送的字符串"Thank you " 。这时需要调用shutdown函数,只关闭服务器的输出流( 半关闭)。这样既可以发送EOF, 同时又保留了输入流,可以接收对方数据。

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/88951558