抓包分析TCP三次握手四次挥手全过程,教你观看“多包运动”的正确姿势
目录
- 抓包过程
- TCP 首部分析
- TCP 三次牵手过程
- TCP 四次分手过程
- 常见面试经典问题
- 为什么牵手是三次,分手是四次?
- 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
抓包过程
使用了 Wireshark 进行抓包,用两个最常用的 curl 和 ping 命令来演示抓包情况,开启抓包。
## 先访问我自己的网站首页
curl https://zengzhiqin.kuaizhan.com
## 再查看我自己网站的地址
ping https://zengzhiqin.kuaizhan.com
Wireshark根据 ping 命令得到的地址进行条件过滤,得到上面两个命令所得到的包,主要有 TCP(https基于tcp协议)协议和 ICMP(ping命令是基于 ICMP 协议)协议的包,如下图所示:
抓包分析
TCP 首部分析
TCP包首部内容一一对应
TCP首部
首部内容简单分析:
- 源端口目标端口
包通过传输层千辛万苦找到了机器,机器那么多应用,假如这个时候艾莉给洪世贤发微信撩sao,世贤开了 QQ 又开了微信,这个时候就靠端口号来区分不同服务,QQ和微信的端口号肯定不一样,因此才能消息才能正确找到微信应用。
- 序列号和确认号
数据分段传输过程中,序列号可以保证所有传输的数据按照正常的次序进行重组,而且通过确认保证数据传输的完整性。
- 如图所示,整理分析五个连续的请求包数据的
序列号
和确认号
进行序列号分析:
整理分析如下:
序列号可以用来确定传输内容的位置,传输到哪一个字节了,接收端接收到好根据序列号进行重组数据。相对序列号和相对确认号是用来简化随机序列号和随机确认号的。
下一个请求包的序列号 = 上一个请求包序列号 + 数据长度
- 请求应答过程如下
请求应答
确认号就是用来确认回复哪个序列号的多大的数据包,所以Ack=请求包的Seq+数据长度
。并且通过确认序列号,确认号,数据长度的值,可以确定此数据包是否完整。
- 数据偏移
就是首部长度,因为首部除了 20 字节固定长度之外,还有选项部分,是可选可变长度,所以需要有个偏移量告诉数据首部长度位置。
- 保留
此 6 bit 二进制没有用到
- 状态位
请求建立连接的数据包的状态位
状态含义
小说明:大写的表示状态,如ACK,小写的表示确认号,如Ack;
- 窗口
tcp是可靠传输,有接收缓存和发送缓存,缓存被叫做窗口,通过滑动窗口技术实现可靠传输(这个地方还挺复杂的,下一篇会写关于可靠传输和拥塞控制相关的)
- 检验和
用来保证 TCP 头和数据的内容在抵达目的时的正确性完整性
- 紧急指针
如果设置了 URG 位,这个域将被检查作为额外的指令,告诉 CPU 从数据包的哪里开始读取数据,优先处理此包的命令。插队打乱了原来的队伍顺序,把列宁同志的团队人员单独优先处理,需要记录下这部分人的信息,是这个团队的就先走。
- 选项
可以规定最大数据报的长度为多少,还可以支持选择性的进行确认。
- 填充
选项和填充一共40个字节,如果不够需要进行填充凑够了
TCP 三次牵手过程
洪世贤饰客户端,林品如饰服务端
三次交涉牵手建立婚姻
牵手过程:
(客户端)世贤:品如,你好美客户端发起请求,头部设置为SYN为1表示他要请求建立连接,并且发送一个随机序列号x,此时世贤进入 SYN_SENT 求婚等待爱情状态;
SYN包:SYN=1, ACK=0, Seq=x
(服务端)品如:世贤,你好帅服务端接收到连接请求,头部设置SYN+ACK表示他要回应了,并且自己也随机生成一个序列号标识自己y,通过将ack确认号设置为客户端的请求序列号x +1 的方式来告诉请求的客户端他回应的是哪个客户端,因为可能有很多个客户端同时向他请求建立连接,进行相应回应,此时品如进入了 SYN_RCVD 收到求婚并且同意等待下一步指示状态
SYN+ACK包:SYN=1, ACK=1, Seq=y, Ack=x+1
(客户端)世贤:品如,我们结婚客户端接收到服务端回复之后,再次确认连接,头部设置为ACK表示他收到了服务端的确认请求现在进行确认,ACK为1表示他要进行确认了,ack=y+1=服务端的请求序列号+1,表示他确认的是服务端这个包的请求,seq=x+1因为上一个客户端请求序列号是x,这个请求包从 x+1 开始,此时世贤收到同意回复进入了宣布结婚 ESTABILSHED 状态, 品如收到了结婚请求也进入了结婚状态
ACK包:ACK=1, Seq=x+1, Ack=y+1
小结:
因为建立连接之前是没有数据传输的,但是 SYN,ACK 等状态需要占用一个序号,所以这个地方请求序列号加的数据都是1,建立连接之后有数据传输,序列号和确认号加的数据都会是数据的长度;
- Ack=请求序列号+数据长度;
- 包的序列号Seq = 同一端上一个包的序列号+数据长度;
- 下一个包的Seq = 上一个应答包的Ack
涨姿势:
借助wireshark的分析工具,点击 wireshark 的 Statistic 下面的Follow Graph,能得到如下的分析图,也可以很直观的看到牵手分手以及序列号:
tcp
TCP 四次分手过程
image
四次分手过程的序列号和确认号,和上面牵手是一样的分析,就不贴图了贴个世贤。
四次挥手过程
(客户端)世贤:你不够骚
(服务端)品如:我的衣柜不好看吗
(服务端)品如:算了衣服你们玩,我去跳海
(客户端)世贤:你喜欢大海,我爱过你
这个分手过程读者自己看图吧,写下来太多了,注意看双方状态,序列号和确认号。
常见面试经典问题
为什么牵手是三次,分手是四次?
三次牵手原因:
一般如果客户端给服务端发起一个请求,服务端回复了,一来一回就能表示网络是畅通的,可以发数据。那么为什么需要第三个数据包呢?
首先,客户端请求建立连接如果没有成功是会一直重试的,那么就会有多个建立连接请求。
世贤和品如的故事
世贤饰客户端A数据包,窃格瓦拉饰客户端B数据包,品如饰服务端
- 假如世贤第一次求爱之前先绕远路去美国买了束玫瑰花,然后 窃格瓦拉 从牢里出来了直接就去找品如求爱了,品如这时候答应了窃格瓦拉的求爱,品如等着和窃格瓦拉来约她看海;
- 然后世贤也到了,品如一看世贤好帅反正她没有结婚又答应了世贤的求爱,于是品如又等着世贤约她看海;
- 试想一下全世界男的都去找品如告白一次,那品如就有看不完的海了,
主要原因是因为品如不知道哪一个男的是来真的
。所以需要有人求婚,也就是第三次确认,结婚了就不用和别的男的看海了,直接放弃掉别人。
如果只有两次,那么服务端回应了就算是建立了连接,但是服务端没法判断他回应的请求连接是否在使用
,这就会导致下面两种情况:
- 第一种是会造成很多无效连接资源不能释放。请求包因为网络慢耗时严重,客户端重复发了很多包,一段时间后这些包到了服务端回复建立起了很多不必要的连接,这些连接资源无法释放,三次牵手第三次再次确认之后服务端建立连接并且将其他的无效连接释放掉。
- 第二种是确认包丢失造成循环死锁问题,如下图
四次分手原因
牵手容易分手难,都市的饮食男女应该都能感受到这点。
主要是因为服务端收到关闭请求之后,服务端的数据可能还没有传完,这个时候服务端会继续把数据传输完,然后再告诉客户端可以关闭了,客户端再关闭。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
MSL指一个片段在网络中最大的存活时间,2MSL是两倍的MSL(Maximum Segment Lifetime),就是一个发送和一个回复所需的最大时间。
网络是不可靠的,如果最后的ACK包丢失了
然后客户端又已经关闭了,那么服务端还会一直发送 FIN 请求关闭但是没人理他,他就会一直发FIN,那么服务端就会一直持续发 FIN 从而没法关闭了。所以客户端需要再等2MSL,这段时间一来一回的路上再也没有 FIN 包过来了,因此客户端可以放心关闭了。
感觉自己在写剧本,满脑子都是品如的衣柜,有收获的老铁`点赞`或者`点在看`来鼓励一下作者吧,感谢观看~
下期预告:
tcp流量控制和拥塞控制的实现
- discuz论坛apache日志hadoop大数据分析项目:hive以及hbase是如何入库以及代码实现
- 【Golang语言社区】前端编程- 从零开始开发一款H5小游戏(一) 重温canvas的基础用法
- 今天聊聊分布式锁 No.86
- 【JS游戏编程基础】关于js里的this关键字的理解
- 【算法】随机森林算法
- 【Golang语言社区--H5编程】smoke.js
- GO语言标准库概览
- Golang测试技术
- 安装Python时遇到如下问题,解决方案
- spring使用Email邮件系统
- discuz论坛apache日志hadoop大数据分析项目:清洗数据核心功能解说及代码实现
- mybatis 对于基本类型数据传值的问题
- Guava------------Cache使用方法
- hadoop安装及配置入门篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 在stm32开发可以调用c标准库的排序和查找 qsort bsearch
- Python解析excel文件并存入sqlite数据库
- python学习总结
- C语言calloc()函数:分配内存空间并初始化——stm32中的应用
- 提升代码的运算速度——代码优化的方法总结
- 自己实现sizeof+大小端测试
- 写一个程序检查一个整数是2的幂
- 持续部署入门:基于 Kubernetes 实现滚动发布
- Python源码分析(一)
- Learning Scrapy(一)
- Python源码分析(二) - List对象
- Python多线程机制
- Python高级用法总结
- Python中的上下文管理器和with语句
- Python中的collections模块