从TCP协议到TCP通信的各种异常现象和分析

news2024/12/26 23:46:06

很多人总觉得学习TCP/IP协议没什么用,觉得日常编程开发只需要知道socket接口怎么用就可以了。如果大家定位过线上问题就会知道,实际上并非如此。如果应用在局域网内,且设备一切正常的情况下可能确实如此,但如果一旦出现诸如中间交换机不稳定、物理服务器宕机或者其它异常情况时,此时引起的问题如果只停留在套接字接口的理解层面将无法解决。因此,深入理解TCP/IP协议,对我们分析异常问题有很大的帮助。

下图是网络通信中常见的架构,也就是CS架构。其中程序包括两部分,分别为客户端(Client)和服务端(Server)。当然,实际的环境还要复杂的多,在客户端和服务端之间可能有多种不同种类和数量的设备,这些设备都会增加网络通信的复杂性。自然,也会增加程序开发容错的复杂性。

图1 基本架构

TCP的基本流程

在分析异常情况之前,我们先回忆一下TCP协议的基本逻辑。在客户端和服务端能够收发数据之前首先必需建立连接。连接的建立在协议层面也是通过收发数据包完成,只不过在用户层面就是客户端调用了一个connect函数。连接的过程俗称“三次握手”,具体流程如图2所示。

图2 TCP的三次握手流程

TCP连接的断开也是比较复杂的,需要经过所谓的“四次挥手”的流程。其原因是因为TCP是双工通信,分别需要从客户端和服务端2侧断开连接。

图3 TCP的四次挥手

另外一个比较重要的内容是TCP协议的状态转换,理解了这个内容,我们才能清楚出现各种异常情况下数据包的内容。

图4 TCP状态转换图

本文只是简单回忆一下TCP的基本流程

异常情况分析

了解了TCP的基本流程之后,我们再看一下各种异常情况。这些异常情况才是我们在后续解决问题的时候的关键。了解了这些异常情况及原理,后面解决问题才能游刃有余。

1. 试图与一个不存在的端口建立连接(主机正常)

这里的不存在的端口是指在服务器端没有程序监听在该端口。我们的客户端就调用connect,试图与其建立连接。这时会发生什么呢?

这种情况下我们在客户端通常会收到如下异常内容:

[Errno 111] Connection refused(连接拒绝) 具体含义可以查一下Linux的相关手册,或者用搜索引擎搜索一下。试想一下,服务端本来就没有程序监听在这个接口,因此在服务端是无法完成连接的建立过程的。我们参考‘三次握手’的流程可以知道当客户端的SYNC包到达服务端时,TCP协议没有找到监听的套接字,就会向客户端发送一个错误的报文,告诉客户端产生了错误。而该错误报文就是一个包含RST的报文。这种异常情况也很容易模拟,我们只需要写一个小程序,连接服务器上没有监听的端口即可。如下是通过wireshark捕获的数据包,可以看到红色部分的RST报文。

图5 数据包截图

继续深入理解一下,在操作系统层面,TCP的服务端实际上就是从网卡的寄存器中读取数据,然后进行解析。对于TCP自然会解析出目的端口这个关键信息,然后根据这个信息查看有没有这样的套接字。这个套接字是什么呢?在用户层面是一个文件句柄,但在内核中实际是一个数据结构,里面记录了很多信息。这个数据结构存储在一个哈希表中,通过函数__inet_lookup_skb(net/inet_hashtables.h)可以实现对该数据结构的查找。对于上述情况,自然无法找到该套接字,因此TCP服务端会进行错误处理,处理的方式就是给客户端发送一个RST(通过函数tcp_v4_send_reset进行发送)。

2. 试图与一个某端口建立连接但该主机已经宕机(主机宕机)

这也是一种比较常见的情况,当某台服务器主机宕机了,而客户端并不知道,仍然尝试去与其建立连接。这种场景也是分为2种情况的,一种是刚刚宕机,另外一种是宕机了很长时间。为什么要分这2种情况?

这主要根ARP协议有关系,ARP会在本地缓存失效,TCP客户端就无法想目的服务端发送数据包了。

(192.168.1.100) 位于 08:00:27:1a:7a:0a [ether] 在 eth0 了解了上述情况,我们分析一下刚刚宕机的情况,此时客户端是可以向服务端发送数据包的。但是由于服务器宕机,因此不会给客户端发送任何回复。

图6 数据包截图

由于客户端并不知道服务端宕机,因此会重复发送SYNC数据包,如图6所示,可以看到客户端每隔几秒会向服务端发送一个SYNC数据包。这里面具体的时间是跟TCP协议相关的,具体时间不同的操作系统实现可能稍有不同。

3. 建立连接时,服务器应用被阻塞(或者僵死)

还有一种情况是在客户端建立连接的过程中服务端应用处于僵死状态,这种情况在实际中也会经常出现(我们假设仅仅应用程序僵死,而内核没有僵死)。此时会出现什么状态?TCP的三次是否可以完成?客户端是否可以收发数据?

在用户层面我们知道,服务端通过accept接口返回一个新的套接字,这时就可以和客户端进行数据往来了。也就是在用户层面来说,accept返回结果说明3次握手完成了,否则accept会被阻塞。在我们假设的情况下,其实就相当于应用程序无法进行accept操作了。

如果想彻底理解上面我们假设的问题,需要理解两点,一点是accept函数具体做了什么,另外一点是TCP三次握手的本质。

我们先试着理解第一点,accept会通过软中断陷入内核中,最终会调用tcp协议的inet_csk_accept函数,该函数会从队列中查找是否有处于ESTABLISHED状态的套接字。如果有则返回该套接字,否则阻塞当前进程。也就是说这里只是一个查询的过程,并不参与三次握手的任何逻辑。

三次握手的本质是什么呢?实际上就是客户端与服务端一个不断交流的过程,而这个交流过程就是通过3个数据包完成的。而这个数据包的发送和处理实际上都是在内核中完成的。对于TCP的服务端来说,当它收到SYNC数据包时,就会创建一个套接字的数据结构并给客户端回复ACK,再次收到客户端的ACK时会将套接字数据结构的状态转换为ESTABLISHED,并将其发送就绪队列中。而这整个过程跟应用程序没有半毛钱的关系。

当上面套接字加入就绪队列时,accept函数就被唤醒了,然后就可以获得新的套接字并返回。但我们回过头来看一下,在accept返回之前,其实三次握手已经完成,也就是连接已经建立了。

图7 TCP缓存与应用进程

另外一个是如果accept没有返回,客户端是否可以发送数据?答案是可以的。因为数据的发送和接受都是在内核态进行的。客户端发送数据后,服务端的网卡会先接收,然后通过中断通知IP层,再上传到TCP层。TCP层根据目的端口和地址将数据存入关联的缓冲区。如果此时应用程序有读操作(例如read或recv),那么数据会从内核态的缓冲区拷贝到用户态的缓存。否则,数据会一直在内核态的缓冲区中。总的来说,TCP的客户端是否可以发送数据与服务端程序是否工作没有任何关系。

当然,如果是整个机器都卡死了,那就是另外一种情况了。这种情况就我们之前分析的第2种情况一直了。因为,由于机器完全卡死,TCP服务端无法接受任何消息,自然也无法给客户端发送任何应答报文。

上面分析了在连接过程中的各种异常,下面重点介绍的是在数据传输过程中的各种异常,以及出现异常后的TCP连接的情况。

相关视频推荐:

tcpip,accept,11个状态,细枝末节的秘密,还有哪些你不知道

网络原理tcp/udp,网络编程epoll/reactor,面试中正经“八股文”

免费学习地址:c/c++ linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加群812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

异常情况分析

这里的分析假设连接已经建立,目前正在数据收发过程。这种情况下会出现各种异常,比如服务器宕机、进程crash或者进程被kill等等。下面我们分别介绍上述集中情况在TCP通信中的表现。

服务进程crash

服务进程crash恐怕是我们日常生成环境最长遇到的情况,没有之一吧。那么在这种情况下客户端软件是什么反应?客户端是否可以感知?

我们分别写客户端和服务端的程序,客户端不断的发送数据,服务端接收数据。异常的模拟很简单,我们可以在服务端制造一个指针访问异常。此时服务端的程序就会crash掉。然后我们观察客户端的表现。先上结果,客户端的表现如下图所示。

可以看到客户端被reset掉了。我们在结合通过wireshark抓获的此时的数据报文内容,可以看到是一个RST报文。

回忆一下什么情况下服务端会发送RST报文。这种场景跟我们前文介绍的服务端没有监听的情况是类似的。由于服务端程序crash了,此时在操作系统中的套接字数据结构已经被释放,因此在协议层收到数据包的时候无法找到对应的套接字进行处理,于是发送了一个RST报文。

手动杀死服务端应用

这也是线上比较常见的操作,当一个模块上线时,ops同学总是会先把旧的进程杀死,然后再启动新的进程。那么在这个过程中TCP连接又会发生了什么呢?是否会像上一种情况一样被RST呢?同样,我们先看一下结果,如下是客户端的情况。

从上面错误码来看是管道破裂,其实也就是连接被中断了。我们再看一下通过wireshark的抓包结果可以看出服务端发送了一个FIN报文,这个报文表示服务端发起了关闭的请求。而接下来的一个报文是客户端对该请求的确认。

所以,从上面客户端的错误码和报文情况我们可以知道,在kill进程时TCP协议是能够感知到的,并且发送的FIN报文。

我们再进一步的思考一下,为什么kill进程会有FIN呢?这个与前面crash的差异在哪?其实kill进程是通过shell想内核发送了SIGKILL或者SIGTERM,内核接收到该信号之后会进行相应的扫尾工作,因此可以看到服务端发送了FIN报文。

Server进程所在的主机关机

主机关机(这里指手动关机)的情况与进程被kill是类似的。这时因为在系统关闭时,init进程会给所有进程发送SIGTERM信号,等待一段时间(5~20秒),然后再给所有仍在运行的进程发送SIGKILL信号。当服务器进程死掉时,会关闭所有文件描述符。带来的影响和上面杀死server相同。

Server进程所在的主机宕机

这是我们线上另一种比较常见的状况。即使宕机是一个小概率事件,线上几千台服务器动不动一两台挂掉也是常有的事。这里挂掉其实包括2种情况,一种是内核panic,另外一种情况是出现了掉电。对于内核panic的情况不会像关机那样会预先杀死上面的进程,而是突然性的。那么此时我们的客户端准备给服务器端发送一个请求,它由write写入内核,由TCP作为一个报文发出,但因为主机已经挂掉,因此客户端无法收到ACK。于是客户端TCP持续重传分节,试图从服务器上接收一个ACK,然而服务器始终不能应答,重传数次之后,大约几分钟才停止,之后返回一个ETIMEDOUT错误。在这种情况下,如果我们调用的是同步发送接口,则在发送缓冲区慢的情况下会阻塞在这里,导致程序阻塞。

这个时间真的很长,对于某些应用这种长时间的卡顿是不能接受的。因此,需要一种手段处理这种情况,在套接字接口中可以通过SO_SNDTIMEO标记进行设置。但是有利也有弊,如果设置了该参数,可能会出现这的数据发送超时的情况,进而出现向服务端发送重复数据的情况,此时需要服务端做去重处理。

服务器进程所在的主机宕机后重启

在客户端发出请求前,服务器端主机经历了宕机—重启的过程。当客户端TCP把分节发送到服务器端所在的主机,服务器端所在主机的TCP丢失了崩溃前所有连接信息,即TCP收到了一个根本不存在连接上(也就是我们前文介绍的查找不到socket数据结构)的报文,所以会响应一个RST分节。

至此,关于TCP协议中各种异常情况介绍完了,详细了解这些内容后对后续线上问题的分析和解决会有很大的帮助。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594986.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Zigbee Install code的使用和CRC的计算

Zigbee Install code的使用和CRC的计算 前言什么是Install codes?Zigbee install codes的格式CRC算法信息:python演示:Install Code的使用:烧录Install codes到Silicon labs EFR32设备中去安装代码文件格式烧录Install codes:核查…

【学习笔记】【万字长文】linux三剑客学习笔记

前言 ​ 上班以后用到的服务器大多数是centos的,很多命令会用一部分但稍微复杂一点的只能问度娘了。 ​ 时间长了,还是没积攒下什么本事,每次都需要百度查找。 ​ 终于有时间整理一篇关于linux三剑客的笔记,作为记录方便以后查…

项目经验分享:LVGL编程举例

本文介绍如何在成功移植LVGL的基础之上,编写自己的LVGL GUI程序。 文章目录 1. LVGL组件简介与LVGL仿真1.1 LVGL组件1.2 LVGL仿真 2. 代码结构3. 编程目标4. 编程前的准备5. LVGL编程基础5.1 简单示例代码5.2 设置组件位置5.3 图片的显示5.4 组件的事件响应5.5 设置…

【P46】JMeter 响应断言(Response Assertion)

文章目录 一、响应断言(Response Assertion) 参数说明二、准备工作三、测试计划设计3.1、包括3.2、匹配3.3、相等3.4、字符串3.5、字符串3.6、或者 一、响应断言(Response Assertion) 参数说明 可以对 Jmeter 取样器的响应消息进…

0x10 会话服务 DiagnosticSessionControl

0x10 会话服务 相当是一种分类,通过控制会话模式,使用不同的服务。应始终只有一个诊断会话在服务器中处于活动状态。 服务器应在启动时始终启动默认的诊断会话。 如果没有启动其他诊断会话,则只要服务器通电,默认诊断会话就应该运…

Hudi(五)集成Flink(3)

12、离线compaction MOR表的compaction默认是自动打开的,策略是5个commits执行一次压缩。因为压缩操作比较耗费内存,和写流程放在同一个pipeline,在数据量比较大的时候(10w/sqps),容易干扰写流程&#xff0…

LLM:LoRA: Low-Rank Adaptation of Large Language Models

随着模型规模的不断扩大,微调模型的所有参数(所谓full fine-tuning)的可行性变得越来越低。以GPT-3的175B参数为例,每增加一个新领域就需要完整微调一个新模型,代价和成本很高。 为解决微调大规模语言模型到不同领域和…

西南交通大学智能监测 培训课程练习3

2023.05.31培训 task1:MybatisPlus的使用 task2:SpringMVC常用接口开发 task3:JSON、接口测试 task4:SpringMVC拦截器与过滤器 目录 一、MybatisPlus 1.1DO类 1.2Mapper接口 1.3编写测试类测试 1.4Mybatis和MybatisPlus 二、…

Verilog学习笔记(串口RS232,基于野火教程)

目录 一、串口简介 二、设计与实现 串口数据回环顶层模块设计 串口接收模块uart_rx 串口发送模块uart_tx 顶层模块rs32_top 三、上板验证 一、串口简介 其中SPI和I2C为同步通信接口,双方时钟频率相同。而UART属于异步通信接口,没有统一时钟&…

旅游网项目(SpringBoot2.7.1 + SpringMVC + Mybatis-Plus3.5.0)

技术选型 JAVA版本:JDK17 数据库:Mysql5.7Navicat 后端框架:SpringBoot3.0.6 SpringMVC Mybatis-Plus3.5.0 权限控制:SpringSecurity 前端框架:AdminLTE2 模板引擎:Thymeleaf 工具类:发…

WIN10系统解决IDEA动不动就卡顿一下

1、前言 不知为啥,最近idea一直在卡顿,输入几个代码都会卡两秒,鼠标滚动文件卡两秒,点击打开文件卡两秒,就算是点击上方的工具栏,它也要等两秒才会出来菜单! 卡顿的时候整个idea直接无响应&a…

Java性能权威指南-总结3

Java性能权威指南-总结3 性能测试方法原则4:尽早频繁测试小结 Java性能调优工具箱操作系统的工具和分析CPU使用率 性能测试方法 原则4:尽早频繁测试 这是最后的原则。性能测试应该作为开发周期不可或缺的一部分。理想情况下,在代码提交到中心源代码仓库前&#xf…

【数据分享】1929-2022年全球站点的逐月最高气温(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标,其中又以气温指标最为常用!说到气温数据,最详细的气温数据是具体到气象监测站点的气温数据! 之前我们分享过1929-2022年全球气象站…

Win10搭建Nacos2.2.3集群版

Nacos是Alibaba提供的服务注册发现的管理平台,其优异的性能越来越受到广大开发者的喜爱,在构建分布式微服务项目中通常会首选Nacos作为注册/配置中心,在实际开发中为了提升服务的可用性和稳定性,通常都会搭建集群版,有…

《强风吹拂》呐!你喜欢跑步吗?

《强风吹拂》呐!你喜欢跑步吗? 三浦紫苑,1976生于东京。主要作品有《多田便利屋》《强风吹拂》《哪啊哪啊~神去村》《编舟记》等 林佩瑾、李建铨、杨正敏 译 文章目录 《强风吹拂》呐!你喜欢跑步吗?[toc]动漫摘录箱根驿…

Go Web下gin框架使用(一)

〇、前言 在前面,已经在这篇文章中详细地讨论了 gin 框架下的模板渲染问题,这篇文章主要对 gin 框架的使用进行讨论。 一、不同的路由 以下可以选择不同的路由进行渲染: r : gin.Default()type usr struct {Name string json:"name&…

八、go语言键盘输入和打印输出

键盘输入和打印输出 一、打印输出 1.1 fmt包 fmt包实现了类似C语言printf和scanf的格式化I/O。格式化verb(‘verb’)源自C语言但更简单。 详见官网fmt的API:https://golang.google.cn/pkg/fmt/ 1.2 导入包 import "fmt"1.3 常…

MyBatis——MyBatis项目搭建

但凡是框架,使用都是分三步走 1.导入jar文件,用maven导入 2.处理配置文件 3.开发业务代码 1.创建maven项目导入相关依赖 在pom文件中导入MyBatis相关依赖jar文件 安装lombok 在File->Settings Pugins 中安装lombok 要想启动lombok的话还需要在B…

GPT带你飞:Chat GPT吊打面试官,实时获取答案,分享调用OpenAI API key+完整源码脚本哦!

目录 福利:文末纯分享中文版CHAT GPT镜像,不存在魔法,纯分享免费使用 故事发生了 火爆GitHub 所以大家注意 网友看了之后调侃到,为了防止线上面试作弊,以后只好把面试都改成线下了。 如何安装 既然是调用GPT的AP…