目录
1.什么是优雅关闭套接字?
优雅关闭套接字指的是在关闭套接字时,双方通信的应用程序会进行一系列协商和处理,以确保数据传输的完整性和可靠性,避免数据的丢失或者损坏。 在传输数据时,通信的两端可能还有未完成的数据需要传输,如果直接关闭套接字,这些数据可能会丢失。因此,在关闭套接字之前,需要通信双方先协商确定是否还有未完成的数据需要传输,如果有,需要先将这些数据传输完成,然后再进行正式的关闭。这个过程称为优雅关闭套接字。
优雅关闭套接字指的是TCP套接字,UDP直接调用close函数关闭套接字。
2.close函数详解
2.1 close函数
close函数会释放该文件描述符所占用的资源,并将文件描述符从进程的文件描述符表中移除。
如果该文件描述符是最后一个引用该文件的文件描述符,close函数会将该文件从内核中移除。
#include <unistd.h>
int close(int fd);
参数:
fd: 要关闭的文件描述符
返回值:
成功:返回值为0
失败:返回值为-1,错误码保存在errno变量中
2.2 close函数实现原理
图 1 close函数实现原理
3.SO_LINGER套接字选项
SO_LINGER是套接字选项之一,用于设置socket关闭时的行为。
SO_LINGER选项由结构体struct linger定义
struct linger {
int l_onoff;
int l_linger;
};
l_onoff:0:关闭SO_LINGER选项,1:开启SO_LINGER选项
l_linger:超时时间,单位秒
SO_LINGER不同参数行为如下表:
SO_LINGER使用示例:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct linger so_linger;
so_linger.l_onoff = 1; //开启
so_linger.l_linger = 5; //超时时间5秒
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
4.shutdown函数详解
4.1 shutdown函数
shutdown函数通过向套接字发送一个特定的信息来关闭连接。它并不是立即关闭,而是等待所有数据发送完成后再关闭。如果套接字上还有未发送的数据,那么它们将在关闭之前被发送出去。
注意,如果套接字已经被关闭,则调用shutdown函数将会导致错误返回。
#include <unistd.h>
int shutdown(int sockfd, int how);
参数:
sockfd:套接字的文件描述符
how:指定关闭方式
SHUT_RD:关闭该套接字的读功能
SHUT_WR:关闭该套接字的写功能,TCP套接字进入half-close状态
SHUT_RDWR:同时关闭该套接字的读写功能
返回值:
成功:返回0
失败:返回-1,并设置errno
4.2 shutdown函数实现原理
图 2 shutdown函数实现原理
5.如何优雅关闭套接字?
5.1 优雅关闭套接字需要满足什么要求?
- 保证双方数据不丢失
- 保证双方套接字资源和文件描述符正确释放
5.2 直接调用close函数会有什么问题?
a.close存在引用计数的问题,如果引用计数不为0,可能不会对套接字做释放操作。
b.close会直接清空套接字资源,此时套接字无法再接收对端数据,导致接收数据丢失。
c.close可能会发送RST报文给对端,对端收到RST报文后也是直接释放套接字资源。
5.3 优雅关闭套接字流程
1.调用shutdown函数关闭套接字写功能,发送FIN报文,完成四次挥手正常关闭套接字。
2.接收剩余数据,防止数据丢失
2.调用close函数,释放套接字资源,关闭文件描述符。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string/h>
#include <sys/socket.h>
#define MAX_LEN (2048)
int my_shutdown(int sockfd) {
//关闭套接字写功能,TCP套接字进入半关闭状态
if (shutdown(sockfd, SHUT_WR) == -1) {
return -1;
}
//接收剩余数据
char buf[MAX_LEN] = {0};
ssize_t len = 0;
do {
memset(buf, 0, MAX_LEN);
len = recv(sockfd, buf, MAX_LEN);
//处理数据
} while(len <= 0);
//关闭套接字,释放套接字资源
if (close(sockfd) == -1) {
return -1;
}
return 0;
}