TCP/IP协议
TCP
概述
TCP/IP协议栈时一系列网络协议的总和,是构成网络通信的核心骨架,定义了电子设备如何连入互联网,以及数据如何在它们之间进行传输。
TCP/IP协议采用4层结构,分别是应用层,传输层,网络层,链路层,物理层
,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
简介
TCP是面向连接, 可靠的, 面向流
的传输协议.
TCP和UDP都是传输层
协议.
传输方式的分类
面向有链接型
和 面向无连接型
两类
TCP数据包
应用层发送数据, 首先经过传输层进行包装 添加TCP等信息, 在经过网络层添加IP等信息, 最后链路层进行包装
TCP首部最小20
个字节,分为5层
, 每层4个字节 即32位.
第一层: 源端口号 + 目标端口号 分别占用16位.
第二层: 32位序列号
- TCP使用序列号对数据进行标记, 便于到达目的后重新组装.
第三层: 32位确认号
- 确认号用于表示期望收到对方下一次报文段的序号值
第四层: 4位首部长度 + 6位保留 + 6位标识 + 16位窗口大小
- 4位表示了TCP的报文段的首部长度, 指出了
TCP数据起始处
到TCP报文起始处
的长度 - 6位用于保留使用
- 6个标志位, 每个1bit
- 4位表示了TCP的报文段的首部长度, 指出了
第五层: 16位校验和 + 16位紧急指针
序列号
序列号占32位, 用于对数据进行标记, 便于到达目的后重新组装
确认号
确认好占32位, 用于表示期望收到对方下一次报文段的序号值
标志位
一共有六个标志位, 每一位1个bit. 分别是: ACK, RST, FIN, SYN, PSH, URG
.
- ACK: 确认序号有效
- RST: 重置连接
- SYN: 发起新连接
- FIN: 释放一个连接
- URG: 紧急消息
- PSH: 缓冲区未填满
当TCP连接建立后, 传送的所有报文段的ACK必须设置为1.
PSH=1 表明该报文段高优先级, 不用等待缓冲区填满.
窗口大小
窗口大小位于第4层, 占16位.
主要用于告知对方端: 当前端的TCP缓冲区还能容纳多少数据. 可以控制发送数据的速度.
校验
校验占用两个字节, 16位,位于第五层.
主要用于结合CRC算法校验TCP的报文是否损坏过. 校验范围: 首部+数据
紧急指针
紧急指针占2个字节, 16位当URG=1时才有效.
用于表示当前报文段中紧急数据的大小.
TCP为什么可靠?
因为TCP使用了三次握手才建立了连接.
C代表客户端, S代表服务端
三次握手
C —-> SYN K —-> S
C <—- SYN N + ACK K + 1 <—- S
C —-> ACK N + 1 —-> S
四次握手
客户端主动发起断开操作:
C —-> FIN K —-> S
C <—- ACK K + 1 <—- S : 客户端端和服务端都进入等待状态
C <—- FIN N <—- S : 客户端进入TIME_WAIT
, 等待2MSL时间
C —-> ACK N + 1 —-> S :
MSL 表明最长报文段寿命.
等待2MSL?
当C进入TIME_WAIT时, 需要再次向S响应一个ACK报文段, 表明客户端收到了关闭请求.
- 保证此次连接的数据段在网络中消失
- 保证TCP协议的全双工连接能够可靠关闭
TCP拥塞控制
当网络出现阻塞, 发送的数据会丢失, 此时发送方会重试, 从加重拥塞. 解决此问题一共有四种算法:
慢开始, 拥塞避免, 快重传, 快恢复
.
慢开始和拥塞避免
发送方会维护一个发送窗口的状态变量(cwnd
).
当建立连接后首次发送数据时进入慢开启 cwnd=1
, 当收到确认后 cwnd 开始成倍递增. 随着报文段数量上升, 网络的拥塞的可能性越大, 当cwnd达到一个阈值后(ssthresh 同时进入拥塞避免
), 则cwnd将不会成倍增长, 而是+1. 如果超时则cwnd /= 2.
快重传/快恢复
当接受方收到报文后, 主要向发送方发送确认消息, 确认最后一个有序报文段
. 如果发送方收到对某个报文段的多次且重复的确认,则会进入快重传
, 重新发送丢失的报文段.
而由于报文段丢失, 接受方位收到确认, 则可能重新进入慢开始. 此时则需要执行快恢复
. 将cwnd = ssthresh = cwnd / 2
.
TCP粘包和拆包
粘包就是连续发送的两个数据包 粘到了一块. 本来接受方需要接受两次的, 发生粘包后一次就可以了.
拆包就是连续发送的两个数据包, 其中一个被拆成了两部分. 例如: packetA 被拆成了 packetA.1 packetA.2 两个.
为什么?
发送的数据小于TCP的缓冲区会发生拆包
发送的数据大于MSS(报文最大长度)会发生拆包
发送的数据小于TCP的缓冲区会发生粘包 或 TCP将多次写入缓冲区的数据一次刷出也会发生粘包
接受方没有及时读取接受缓冲区的中的数据
解决办法
由于TCP是面向流的, 所以从底层是无法解决的. 只能从上层(应用层)解决.
- 消息定长: 发送的数据包长度一致, 不够的话填充0
- 设置消息边界: 类似于标识, 例如FTP使用
回车换行符
来进行数据分割. - 消息分割: 将消息分为
头
和体
. 头中会保存消息体的长度.
总结
首先梳理一下每层模型的职责:
- 链路层:对0和1进行分子,定义数据帧,确认主机的物理地址,传输数据。
- 网络层:定义IP地址,确认主机所在网络位置,并通过IP进行MAC寻址,对外网数据包进行路由转发。
- 传输层:定义端口,确认主机上应用程序身份,并将数据包交给对应的应用程序。
- 应用层:定义数据格式,并按照对应的格式解读数据。