参考:《UNIX 网络编程 · 卷1 : 套接字联网API》
TCP 半关闭
如果将客户端与服务器之间的网络作为全双工管道来考虑,请求是从客户端向服务器发送,应答是从服务器向客户端发送,其如下图所示:
上图假设 RTT 为 8,且服务器没有处理时间且请求大小与应答大小相同。既然从管道发出到管道的另一端存在延迟,而管道是全双工的,仅仅使用了管道的 1/8,对于停-等方式对于交互式输入是合适的。
如果以批量的方式输入,客户端以网络可接受的最快速度持续发送请求,服务器也以相同的速度处理它们并发回应答。这就将导致在时刻7的时候,管道充满,如下图所示:
在上图中,假设在时刻8的时候,发出一个请求,我们并不能立即关闭连接,因为管道中还有其他的请求和应答,这时如果程序直接从 main 结束,并不意味着同时完成了从套接字的读入,可能有请求在去往服务器的路上,或者仍然有应答在返回客户的路上。
我们需要一种关闭 TCP 连接其中一半的方法。也就是说,要给服务器发送一个 FIN,告诉服务器已经完成了数据发送,但仍然保持套接字打开以便读取,这将由 shutdown
函数完成。
shutdown 函数
终止网络连接的通常方法是调用 close 函数。但是 close 函数有两个限制,却可以使用 shutdown 函数来避免:
- close 把描述符的引用计数减一,仅在该计数变为0时才关闭套接字。但是使用 shutdown 中可以不管引用计数就激发 TCP 的正常连接终止序列。
- close 终止读和写两个方向的数据传送。既然 TCP 连接是全双工的,有时候我们需要告知对端我们已经完成了数据发送,即使对端仍有数据要发送给我们。
shutdown函数声明:
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
- 1
- 2
该函数的行为依赖于howto
参数的值:
SHUT_RD:关闭连接的读这一半。套接字中不再有数据可接收,而且套接字接收缓冲区中的现有数据被丢弃。进程不再对这样的套接字调用任何读函数。对一个 TCP 套接字这样的调用 shutdown 函数后,由该套接字接收的来自对端的任何数据都被确认,然后悄然丢弃。
SHUT_WR:关闭连接的写这一半。对于 TCP 套接字,这成为半关闭。当前留在套接字发送缓冲区中的数据将被发送掉,后跟 TCP 的正常连接终止序列。不管套接字描述符的引用计数是否等于 0,这样的写半部分照样执行。进程不能再对这样的套接字调用任何写函数。
SHUT_RDWR:连接的读半部分和写半部分。这与调用 shoudown 函数两次等效:第一次指定 SHUT_RD,第二次调用指定 SHUT_WR。
shutdown 和 colse 的操作和 SO_LINGER 套接字选项的值有关。
TCP 半连接
半连接发生在 TCP 三次握手中。
如果 A 向 B 发起链接,B 也按照正常情况响应了,但是 A 不进行三次握手,这就是半连接。
半连接攻击:半连接,会造成 B 分配的内存资源就一直这么耗着,直到资源耗尽。(也被称为 SYN攻击)
TCP 半打开
如果一方关闭或者异常关闭(断电,断网),而另一方并不知情,这样的链接称之为半打开。处于半打开的连接,如果双方不进行数据通信,是发现不了问题的,只有在通信是才真正的察觉到这个连接已经处于半打开状态,如果双方不传输数据的话,仍处于连接状态的一方就不会检测另外一方已经出现异常
解决方法:
如何解决半打开问题,引入心跳机制就可以察觉半打开。
如果需要发数据的话,这边收到之后 其实发现这个连接并不存在了,就会回复 RST 包告知,这个时候就需要重新建立连接了。
</article>