优雅的关闭连接 --- 使用shutdown和setsockopt(SO_LINGER)实现

优雅关闭:如果发送缓存中还有数据未发出则将其发出去,并且收到所有数据的ACK之后,发送FIN包,开始关闭过程。TCP连接线关闭一个方向,此时另外一个方向还是可以正常进行数据传输。

强制关闭:如果缓存中还有数据,则这些数据都将被丢弃,然后发送RST包,直接重置TCP连接。两边都关闭了,服务端处理完的信息没有正常传给客户端。

一、使用shudown优雅的关闭连接
1.1、close()函数:

#include <unistd.h>
int close(int fd);

close()函数会使套接字的引用计数减1,若套接字的引用计数为0,则彻底关闭连接,并且会关闭TCP两个方向的数据流,既不能发送数据也不能接收数据。close函数不能关闭一个方向上的连接,而shutdown函数可以实现。如果服务端还有没有处理完的数据,不能正常发送给客户端。

1.2、shutdown函数

int shutdown(int sock, int howto); 

参数howto的取值:
    SHUT_RD:断开输入流。套接字无法接收数据(即使输入缓冲区收到数据也被抹去),无法调用输入相关函数。
    SHUT_WR:断开输出流。套接字无法发送数据,但如果输出缓冲区中还有未传输的数据,则将传递到目标主机
    SHUT_RDWR:同时断开 I/O 流。相当于分两次调用 shutdown(),其中一次以 SHUT_RD 为参数,另一次以 SHUT_WR 为参数。

1.3、close()和shutdown()的区别

  • close会关闭连接,并释放所有连接对应的资源,而shutdown并不会释放掉套接字和所有资源。
  • close有引用计数,例如父子进程都打开了某个文件描述符,其中某个进程调用了close函数,会使close函数的引用计数减1,直到套接字的引用计数为0,才会真正的关闭连接。而shutdown函数可以无视引用计数,直接关闭连接。
  • close的引用计数的存在导致不一定会发出FIN结束报文,而shutdown一定会发出FIN报文。
  • shutdown() 用来关闭连接,而不是套接字,不管调用多少次 shutdown(),套接字依然存在,直到调用 close() / closesocket() 将套接字从内存清除。
  • 调用 close()关闭套接字时,或调用 shutdown() 关闭输出流时,都会向对方发送 FIN 包。FIN 包表示数据传输完毕,计算机收到 FIN 包就知道不会再有数据传送过来。
  • 默认情况下,close()引用计数为0后会立即往网络中发送FIN包,不管输出缓冲区中是否还有数据,而shutdown() 会等输出缓冲区中的数据传输完毕再发送FIN包。也就意味着,调用 close()将丢失输出缓冲区中的数据,而调用 shutdown() 不会。
     

二、使用setsockopt设置SO_LINGER实现优雅的关闭连接
2.1 LINGER
LINGER结构包含了指定套接字的信息,当调用了close()函数关闭该套接字时,LINGER结构中的信息指定了如何处理未发送的数据。

typedef struct linger {
 
 u_short l_onoff;
 
u_short l_linger;
 
} LINGER, *PLINGER, *LPLINGER;

取值方案:

  • 设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;
  • 设置 l_onoff为非0,l_linger为0,当调用close的时候,TCP连接会立即断开.send buffer中未被发送的数据将被丢弃,并向对方发送一个RST信息.值得注意的是,由于这种方式,不是以4次握手方式结束TCP链接,所以,TCP连接将不会进入TIME_WAIT状态,这样会导致新建立的可能和就连接的数据造成混乱。这种关闭方式称为“强制”或“失效”关闭。
  • 设置 l_onoff 为非0,l_linger为非0,在这种情况下,回事的close返回得到延迟。调用close去关闭socket的时候,内核将会延迟。也就是说,如果send buffer中还有数据尚未发送,该进程将会被休眠直到一下任何一种情况发生:

a. send buffer中的所有数据都被发送并且得到对方TCP的应答消息(这种应答并不是意味着对方应用程序已经接收到数据)

b.延迟时间消耗完。在延迟时间被消耗完之后,send buffer中的所有数据都将会被丢弃。

这种关闭称为“优雅的”关闭。

2.2 setsockopt
setsockopt()函数的作用是设置套接字的选项。

man手册查阅函数原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
 
int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

参数:

sockfd表示要设置选项的套接字;
level指定了选项的级别,对于套接字优雅关闭的选项,该值设置为SOL_SOCKET;
optname表示要设置套接字的选项,该选项必须是在level中定义的,如果设置套接字需要优雅关闭,则该参数的值为SO_LINGER,如果套接字直接关闭,则该参数的值为SO_DONTLINGER;
optval是缓冲区的指针,该缓冲区中包含了选项的值;
optlen是optval指向的缓冲区的大小。
返回值:

如果setsockopt()函数执行成功,则返回值是0,否则返回值为SOCKET_ERROR。
总结:

       通过setsockopt可以设置SO_LINGER,从而实现优雅的关闭连接

示例:

struct linger tmp = {1, 1};
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));

————————————————
版权声明:本文为CSDN博主「六一要努力哦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36316285/article/details/115287746

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值