close
这个函数会对套接字引用计数-1,一旦发现引用计数到0,就会对套接字进行彻底释放,并且会关闭tcp两个方向的数据流。
因为套接字可以被多个进程共享,你可以理解为我们给每个套接字都设置了一个积分,如果我们通过fork的方式创建了子进程,套接字积分+1,如果我们调用一次close,套接字积分-1
close函数具体是如何关闭两个方向的数据流呢?
在输入方向,系统内核会将该套接字设置为不可读,任何操作都会返回异常。
在输出方向,系统内核尝试将发送缓冲区的数据发送给对端,并最后向对端发送一个FIN报文,接下来如果再对该套接字进行写操作,则会返回异常。
如果对端没有检测到套接字已经关闭,还继续发送报文,就会收到一个RST报文,告诉对端:“Hi,我已经关闭了,别再给我发送数据了”。
shutdown
shutdown可以关闭一个连接的方向
int shutdown(int sockfd, int howto)
howto是这个函数的设置选项,它的设置主要有三个选项
SHUT_RD(0):关闭连接的读方向,对该套接字进行读操作直接返回EOF。从数据角度来看,套接字上接收缓冲区已有的数据将被丢弃,如果再有数据到达,会对数据进行ACK,然后悄悄丢弃。也就是说,对端还会收到ACK,在这种情况下不知道发送的数据已经被丢弃了。
SHUT_WR(1):关闭连接的写方向,这就是常被称为半关闭的连接。此时,不管套接字引用计数的值是多少,都会直接关闭连接的写方向。套接字上发送缓冲区已有的数据将被立即发送出去,并发送给对端一个FIN报文。应用程序再对该套接字进行写操作将会报错。还是会接收对端的数据的。
SHUT_RW(2):关闭套接字的读和写两个方向。这个看起来和直接调用close没区别,但是还是有差别的。
1.close会关闭连接,并释放所有连接对应的资源,而shutdown并不会立刻主动释放掉套接字和所有的资源。
2.close存在引用计数的概念,并不一定会导致该套接字不可用, shutdown则不会管引用计数,直接使得该套接字不可用,如果别的进程还在使用该套接字,将会收到影响。
3.close的引用计数导致不一定会发出FIN报文(因为引用计数???),而shutdown则总是会发出FIN报文,这在我们打算关闭连接通知对端的时候,非常重要。
直接调用close和使用shutdown关闭写方向的过程如上图。