TCP中的两类故障模式

简介: 【4月更文挑战第10天】故障分为两大类,一类是对端无 FIN 包,需要通过巡检或超时来发现;另一类是对端有 FIN 包发出,需要通过增强 read 或 write 操作的异常处理,帮助我们发现此类异常。

1、网络中断造成的对端无 FIN 包

很多原因都会造成网络中断,在这种情况下,TCP 程序并不能及时感知到异常信息。除非网络中的其他设备,如路由器发出一条 ICMP 报文,说明目的网络或主机不可达,这个时候通过 read 或 write 调用就会返回 Unreachable 的错误。


可惜大多数时候并不是如此,在没有 ICMP 报文的情况下,TCP 程序并不能理解感应到连接异常。如果程序是阻塞在 read 调用上,那么很不幸,程序无法从异常中恢复。这显然是非常不合理的,不过,我们可以通过给 read 操作设置超时来解决。


如果程序先调用了 write 操作发送了一段数据流,接下来阻塞在 read 调用上,结果会非常不同。Linux 系统的 TCP 协议栈会不断尝试将发送缓冲区的数据发送出去,大概在重传 12 次、合计时间约为 9 分钟之后,协议栈会标识该连接异常,这时,阻塞的 read 调用会返回一条 TIMEOUT 的错误信息。如果此时程序还执着地往这条连接写数据,写操作会立即失败,返回一个 SIGPIPE 信号给应用程序。


2、系统崩溃造成的对端无 FIN 包

当系统突然崩溃,如断电时,网络连接上来不及发出任何东西。这里和通过系统调用杀死应用程序非常不同的是,没有任何 FIN 包被发送出来。


这种情况和网络中断造成的结果非常类似,在没有 ICMP 报文的情况下,TCP 程序只能通过 read 和 write 调用得到网络连接异常的信息,超时错误是一个常见的结果。


不过还有一种情况需要考虑,那就是系统在崩溃之后又重启,当重传的 TCP 分组到达重启后的系统,由于系统中没有该 TCP 分组对应的连接数据,系统会返回一个 RST 重置分节,TCP 程序通过 read 或 write 调用可以分别对 RST 进行错误处理。


如果是阻塞的 read 调用,会立即返回一个错误,错误信息为连接重置(Connection Reset)。


如果是一次 write 操作,也会立即失败,应用程序会被返回一个 SIGPIPE 信号。


3、对端有 FIN 包发出

对端如果有 FIN 包发出,可能的场景是对端调用了 close 或 shutdown 显式地关闭了连接,也可能是对端应用程序崩溃,操作系统内核代为清理所发出的。从应用程序角度上看,无法区分是哪种情形。


阻塞的 read 操作在完成正常接收的数据读取之后,FIN 包会通过返回一个 EOF 来完成通知,此时,read 调用返回值为 0。这里强调一点,收到 FIN 包之后 read 操作不会立即返回。你可以这样理解,收到 FIN 包相当于往接收缓冲区里放置了一个 EOF 符号,之前已经在接收缓冲区的有效数据不会受到影响。


服务器端和客户端程序。

//服务端程序
int main(int argc, char **argv) {
    int connfd;
    char buf[1024];
    connfd = tcp_server(SERV_PORT);
    for (;;) {
        int n = read(connfd, buf, 1024);
        if (n < 0) {
            error(1, errno, "error read");
        } else if (n == 0) {
            error(1, 0, "client closed \n");
        }
        sleep(5);
        int write_nc = send(connfd, buf, n, 0);
        printf("send bytes: %zu \n", write_nc);
        if (write_nc < 0) {
            error(1, errno, "error write");
        }
    }
    exit(0);
}

服务端程序是一个简单的应答程序,在收到数据流之后回显给客户端,在此之前,休眠 5 秒,以便完成后面的实验验证。


客户端程序从标准输入读入,将读入的字符串传输给服务器端:

//客户端程序
int main(int argc, char **argv) {
    if (argc != 2) {
        error(1, 0, "usage: reliable_client01 <IPaddress>");
    }
    int socket_fd = tcp_client(argv[1], SERV_PORT);
    char buf[128];
    int len;
    int rc;
    while (fgets(buf, sizeof(buf), stdin) != NULL) {
        len = strlen(buf);
        rc = send(socket_fd, buf, len, 0);
        if (rc < 0)
            error(1, errno, "write failed");
        rc = read(socket_fd, buf, sizeof(buf));
        if (rc < 0)
            error(1, errno, "read failed");
        else if (rc == 0)
            error(1, 0, "peer connection closed\n");
        else
            fputs(buf, stdout);
    }
    exit(0);
}

4、read 直接感知 FIN 包

我们依次启动服务器端和客户端程序,在客户端输入 good 字符之后,迅速结束掉服务器端程序,这里需要赶在服务器端从睡眠中苏醒之前杀死服务器程序。


屏幕上打印出:peer connection closed。客户端程序正常退出。

$./reliable_client01 127.0.0.1
$ good
$ peer connection closed

这说明客户端程序通过 read 调用,感知到了服务端发送的 FIN 包,于是正常退出了客户端程序。

5、通过 write 产生 RST,read 调用感知 RST

这一次,我们仍然依次启动服务器端和客户端程序,在客户端输入 bad 字符之后,等待一段时间,直到客户端正确显示了服务端的回应“bad”字符之后,再杀死服务器程序。客户端再次输入 bad2,这时屏幕上打印出”peer connection closed“。


屏幕输出和时序图。

$./reliable_client01 127.0.0.1
$bad
$bad
$bad2
$peer connection closed


故障分为两大类,一类是对端无 FIN 包,需要通过巡检或超时来发现;另一类是对端有 FIN 包发出,需要通过增强 read 或 write 操作的异常处理,帮助我们发现此类异常。

相关文章
|
存储 网络协议 Ubuntu
【C++网络编程】Socket基础:网络通讯程序入门级教程
【C++网络编程】Socket基础:网络通讯程序入门级教程
343 7
STM32CubeMX RS485接口使用
STM32CubeMX RS485接口使用
1267 8
|
移动开发 小程序 API
uniapp组件库Popup 弹出层 的使用方法
uniapp组件库Popup 弹出层 的使用方法
1053 1
|
开发工具 数据安全/隐私保护 C++
windows openssl安装和基本使用(代码演示)
本文主要讲到了openssl的基本使用方法,开发环境为windows,开发工具为VS2019.本文主要是说明openssl如何使用,不介绍任何理论知识,如果有不懂的,请自行百度。个人建议下一个everything查询工具,真的很好用,比window自带的查询快了很多,可以查询自己想要的文件
909 0
windows openssl安装和基本使用(代码演示)
|
Shell 索引
shell脚本变量详解(自定义变量、环境变量、变量赋值、变量运算、变量内容替换)
shell变量 shell变量是指用一个特定的字符串去表示不固定的内容 1.变量的类型 1.1自定义变量 一般情况下不怎么使用环境变量,如果需要在其他文件中引入某个文件的变量则在脚本最开始的位置使用source 或者. 执行下该脚本即可
3684 0
shell脚本变量详解(自定义变量、环境变量、变量赋值、变量运算、变量内容替换)
|
存储 缓存 JSON
Kubernetes LIST 请求服务调优
Kubernetes LIST 请求服务调优
727 1
|
开发框架 监控 安全
稳定性专题 | Spring Boot 常见错误及解决方法
导读 『StabilityGuide』是阿里多位阿里技术工程师共同发起的稳定性领域的知识库开源项目,涵盖性能压测、故障演练、JVM、应用容器、服务框架、流量调度、监控、诊断等多个技术领域,以更结构化的方式来打造稳定性领域的知识库,欢迎您的加入。
7451 84
稳定性专题 | Spring Boot 常见错误及解决方法
|
C语言
【C语言】写入访问权限冲突
【C语言】写入访问权限冲突
457 0
《QT从基础到进阶·十二》QPixmap.load加载图片不更新问题
《QT从基础到进阶·十二》QPixmap.load加载图片不更新问题
475 0
|
监控 网络协议 算法
连接重置常见原因及排查方法
与 SYN/FIN 类似,TCP RST 报文也是控制类报文的一种,可以改变TCP 状态也可以用于响应未预期的报文,在TCP Header 中的Flags 字段内标记。相比于其他报文,RST 包是专门为了处理一些异常状态而设计的,通常由协议栈本身使用,业务应当只在“不得不”的情况下使用RST强行终止连接,那么RST 的场景到底有哪些呢,该如何排查RST 问题,其实都是有套路的,请看下文。
24631 4
连接重置常见原因及排查方法