1、关闭方法

从应用层角度来讲,关闭一个TCP链接主要有两种方法,分别是close函数和shutdown函数,他们的函数原型分别为:

#include <unistd.h>
int close(int sockfd);
#include <sys/socket.h>
int shutdown(int sockfd, int howto);



可以看到两者都以一个套接字描述符为参数,只不过前者的目的是为了关闭一个套接字并释放其系统资源,为了达到这个目的必然要关闭与这个套接字对应的链接;而后者则是为了关闭这个套接字对应的链接,如果要关闭套接字并释放资源,还是要调用close,这就是为什么在调用shutdown之后还是要调用close的原因。

这两者在关闭链接时的行为也存在不同:

(1)使用close关闭一个链接时,只是将其参数sockfd的引用计数减一,只有当引用计数减为0的时候才会真正引发关闭链接的行为;而shutdown关闭一个链接时,会直接关闭其参数sockfd对应的链接;这个不同点导致在多进程或者多线程编程时,close可以保证一个进程关闭了某个共享的套接字描述符时,其他进程仍然能使用该描述符对应的链接,因为close只是将描述符的引用计数减一,并没有引发真正的关闭操作;而一个进程使用shutdown关闭链接时,其他使用这个共享套接字描述符的进程将无法使用这个链接;

(2)close关闭链接无法指定关闭的方向,即无法指定关闭读还是关闭写;而shutdown则可以,其第二个参数howto规定了关闭读还是关闭写,其中SHUT_RD代表关闭读,SHUT_WR代表关闭写,SHUT_RDWR代表关闭读写;

2、close和shutdown关闭链接时的具体行为

这里参考的描述,文中对两者的行为总结的很透彻。这里借用上述文章中的两幅图:

(1)close关闭过程


ExecutorService shutdown关闭线程池_描述符

(2)shutdown关闭过程

ExecutorService shutdown关闭线程池_描述符



(3)、总结

a、从上述两幅图中可以看到无论是shutdown还是close,他们发送FIN后返回的情况都紧跟着TCP四次挥手关闭连接的操作,不同之处在于当设定了SO_LINGER选项以及l_linger值不为0时,close必须等待对方对最后发送的数据包以及FIN包确认后才返回,即close的返回标志着四次挥手中的前两次挥手的完成;而其他正常返回(非发送RST返回的情况)则只完成了四次挥手的第一步,发送了FIN包。

b、套接字选项SO_LINGER的作用是对close返回时是否等待对面的确认FIN包作出规定。该选项可用setsockopt函数设置:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)

其中optname为SO_LINGER, optval为设置的选项的值的指针,SO_LINGER选项的值为linger结构体:


struct linger
{
	int l_onoff;	/*表示是否开启选项,0=off, nonzero=on*/
	int l_linger;	/*表示等待的超时时间,POSIX规范里该值的单位为秒*/
}