一、UDP协议
概述
- 定义
- 用户数据报协议(UDP,User Datagram Protocol)是一种传输层通信协议,支持
无连接
面向报文
的尽最大努力交付
服务,并拥有没有拥塞控制
,通信端数量任意
和首部开销小
的特点
- 用户数据报协议(UDP,User Datagram Protocol)是一种传输层通信协议,支持
- 特点
无连接
:发送数据之前不需要建立连接,减少了开销和时延。面向报文
:以报文为单位进行传输,保留了应用层报文的边界,不合并也不拆分报文,提高了可靠性。尽最大努力交付
,即不保证可靠交付,也不进行重传或确认,适合实时应用场景。没有拥塞控制
,因此网络出现拥塞时不会降低发送速率,适合实时应用场景。通信端数量任意
,支持一对一、一对多、多对一和多对多的交互通信。首部开销小
,相比TCP的20字节,UDP只有8个字节,节省带宽资源。
- UDP数据报的组成
- 16位源端口号
- 16位目的端口号
- 16位UDP长度:包含首部和数据的长度
- 16位UDP校验和:检测UDP数据报在传输中是否有错,有错就丢弃
二、TCP协议
概述
- 定义
- 传输控制协议(TCP,Transmission Control Protocol)是一种传输层通信协议,支持
一对一
全双工通信
的可靠交付
服务,使用以字节
为单位的窗口机制,进行流量控制和拥塞控制。
- 传输控制协议(TCP,Transmission Control Protocol)是一种传输层通信协议,支持
- 特点
- 只能一对一通信,每条TCP连接只能是双方
- 提供全双工通信,TCP连接两端都有发送和接收缓存
- 提供可靠交付,保证传输的数据是可靠无差错的
- 面向字节流,传送的数据流中每个字节都有序列号seq
TCP报文段
- 报文段格式
- TCP报文格式(数据结构)详解
- 源端口和目的端口:各占2个字节,收发双方的应用进程标识
- 序号seq:占4个字节,以字节为单位给每次TCP连接中传输的数据进行顺序编号,初始值为系统生成的随机数
- 确认号ack:占4个字节,期望收到对方下一个报文段的数据部分的第一个字节序号,这也表明期望收到序号前的字节数据均已收到。(累积确认,只确认有序队列接收的最后一个,而不是每个都确认,有利于降低开销 )
- 首部长度(数据偏移):占2个字节,TCP报文段中TCP首部所占的长度
- 保留字段:占6位。保留为今后使用,但目前应置为0
- 控制位:每一个标志位表示一个控制功能
- URG:紧急指针标志,为1时表示报文段中有紧急数据,需要配合紧急指针使用
- ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段
- PSH:快推送标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队;
- RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求;
- SYN:同步标志,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1;
- FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。
- 窗口字段:占2个字节,接受方表示的允许对方发送的最大窗口数据量
- 校验和:占2个字节,校验首部和数据两个部分
- 紧急指针:占2个字节,指出紧急数据的大小,约定紧急数据在报文段数据部分首部
- 选项字段:只有
SYN =1
才有用,表示TCP报文段所允许传送的最大数据部分的长度MSS - 填充字段:这是为了使整个首部长度是4字节的整数倍。
- 端口号
- 作用:区分单个设备上的多个应用进程
- 服务端使用的端口号有两类
- 服务端口号,数值为
0~1023
,分配给TCP/IP中的重要程序 - 登记端口号,数值为
1024~49151
- 临时端口号,数值为
49152~65535
,仅在客户程序运行时才动态进行选择
- 服务端口号,数值为
协议 | 名称 | 默认端口 | 底层协议 |
---|---|---|---|
HTTP | 超文本传输协议 | 80 | TCP |
HTTPS | 超文本传输安全协议 | 443 | TCP |
Telnet | 远程登录服务的标准协议 | 23 | TCP |
FTP | 文件传输协议 | 20数据端口 21控制端口 | TCP |
TFTP | 简单文件传输协议 | 69 | UDP |
SMTP | 简单邮件传输协议(发送用) | 25 | TCP |
POP | 邮局协议(接收用) | 110 | TCP |
DNS | 域名解析服务 | 53 | 服务器间进行域传输的时候用TCP 客户端查询DNS服务器时用 UDP |
连接建立
- TCP的连接建立是采用C/S方式进行的
- 三次握手(状态机转换视角,黄色为指令,红色为
状态
)- 前提:客户端处于
打开状态
,服务器处于监听状态
- 第一次:客户端发送TCP连接请求,
由打开状态
并进入同步已发送状态
。- 同步标志SYN=1:表明这是一个握手报文
- 客户端初始序列号seq=x:值由客户端随机生成,避免黑客进行序列号预测并绕过检查劫持TCP连接
- 第二次:服务端发送针对TCP连接请求的确认,并由
监听状态
进入同步已接收状态
。- 控制位SYN=1 和 ACK=1。表示这是一个SYN握手和ACK确认应答报文
- 服务器初始序列号seq=y,值由服务器随机生成
- 确认号ack=x+1,表示收到了客户端的序列号x之前的数据,希望客户端下次发送的数据从x+1开始。
- 第三次:客户端发送确认报文的确认,并由
同步已发送状态
进入连接已建立状态
序列号seq=x+1
,数据部分首字节在本次TCP连接传输中的序号确认号ack=y+1
,表示收到了服务器的y之前的数据,希望服务器下次发送的数据从y+1开始。SYN=0,ACK=1
:表示这是一个确认应答报文,但不是一个握手报文,SYN只在与对方第一次请求建立连接时为1
- 服务器端收到最后的确认报文,由
同步已接收状态
进入连接已建立状态
,双方开始数据传输
- 前提:客户端处于
- 通信双方如何协商TCP的最大报文长度MSS(每次传输数据长度)?
- 前提:
- MSS值由
选项字段
携带传送,只有SYN =1
时,该字段才有用 - MSS = MUT(1500) - IP首部长度(20字节) - TCP首部长度(20字节固定部分)
- tips: 一个标准的以太网的数据帧的大小为1518 :头部有14个字节,尾部的CRC为4个字节,所以留给上层协议数据的大小为1500字节
- MSS值由
- 过程:
- 第一次握手:告知
服务器
最大发送数据大小,MSS值在客户端发送的SYN同步报文的选项段中 - 第二次握手:告知
客户端
最大发送数据大小,当服务器端收到SYN报文后,会向请求端返回SYN+ACK(同步确认报文)报文,其中的“选项”字段也会有MSS值。
- 第一次握手:告知
- 结果:
- 通信双方选择SYN和SYN+ACK报文中
最小的MSS
作为此次TCP连接的MSS - 通信双方的协商,在第二次握手后就可以确定TCP中最大传输报文(MSS)大小
- 通信双方选择SYN和SYN+ACK报文中
- 前提:
- 为什么需要三次握手
- 避免因网络阻塞建立的无效连接
- 如果只有两次握手,每收到一个 SYN 就只能先主动建立一个连接,如果客户端的
SYN报文被网络阻塞
了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
- 如果只有两次握手,每收到一个 SYN 就只能先主动建立一个连接,如果客户端的
- 维护连接的每个方向序列号的请求和确认
- 避免因网络阻塞建立的无效连接
- SYN攻击
- 原理:SYN攻击属于DoS攻击(Denial of Service 拒绝服务)的一种。通过大量发送
伪造源IP的第一次握手同步SYN报文
,服务器每接收到一个SYN包就会为这个连接信息分配核心内存并放入半连接队列,当攻击的SYN包超过半连接队列的最大值时,正常的客户发送SYN数据包请求连接就会被服务器丢弃,导致正常的连接请求无法成功。 - 解决方法
- 限制ip连接次数:比如限制同一IP一分钟内新建立的TCP连接数仅为10,linux可以使用
iptables
设置 - 增大半连接状态的连接数容量:但增大内存资源占用,不推荐
- 延迟分配连接资源:当服务器收到第一次握手请求时,不马上分配TCP连接资源。而是计算一个随机值,在第二次握手时传给客户端,当客户端返回第三次握手时,服务器验证随机值的正确性,确认无误才会进入 TCP 的连接状态,才会分配资源(延迟资源分配思想:写时拷贝、懒汉单例模式)
- 限制ip连接次数:比如限制同一IP一分钟内新建立的TCP连接数仅为10,linux可以使用
- 原理:SYN攻击属于DoS攻击(Denial of Service 拒绝服务)的一种。通过大量发送
- 数据包丢失了该怎么办?
- 第一次握手的 SYN 丢包:
重传
SYN 数据包,重传次数超过阈值后放弃 - 第二次握手的 SYN+ACK 丢包
- 客户端 SYN 包没有收到ACK,也会超时重传
- 服务端 SYN包也没有收到ACK, 也会超时重传
- 第三次握手的 ACK 丢包
- 服务端会一直重传 SYN、ACK 包,重传次数超过阈值后放弃
- 客户端:1. 有保活机制会经过 2 小时 11 分 15 秒发现一个
死亡连接
,于是客户端就会断开连接。2. 无保活机制会一直重传该数据包,直到重传次数超过阈值后就会断开 TCP 连接。
- 第一次握手的 SYN 丢包:
- 一个已经建立的 TCP 连接中,客户端中途宕机了,客户端恢复后,向服务端发送SYN包重新建立连接,此时服务端会怎么处理?
- TCP 连接是由
四元组(客户端的
I
P
、服务端
I
P
、目的端口、源端口)
四元组(客户端的IP、服务端IP、目的端口、源端口)
四元组(客户端的IP、服务端IP、目的端口、源端口)唯一确认的,关键在于本次连接的源端口是否和上一次连接的源端口是否相同
- 相同:客户端发送同步SYN报文后,收到的同步确认报文序列号错误,客户端会
发送RST报文释放连接
- 不相同:服务端认为是要建立新的TCP连接,会进行三次握手。以前的TCP连接会
超时释放
或者客户端收到服务端的询问报文而发送RST报文释放连接
- 相同:客户端发送同步SYN报文后,收到的同步确认报文序列号错误,客户端会
- TCP 连接是由
四元组(客户端的
I
P
、服务端
I
P
、目的端口、源端口)
四元组(客户端的IP、服务端IP、目的端口、源端口)
四元组(客户端的IP、服务端IP、目的端口、源端口)唯一确认的,关键在于本次连接的源端口是否和上一次连接的源端口是否相同
- TCP粘包问题
- 前提:TCP 是
字节流协议
,没有包的概念,粘包问题是应用层
需要进行解决的问题,与TCP协议没有关系。 - 原因:
- 内部粘连:TCP默认的Nagle传输算法会将多个分组
拼装
为一个数据段发送出去 - 外部黏连:一条TCP连接被
多个进程
复用。此时,多种不同结构
的数据会进入到流式传输,出现边界无法分割
问题。
- 内部粘连:TCP默认的Nagle传输算法会将多个分组
- 解决方式
- 固定大小:数据报固定大小,不足的使用特殊字符填充,简单但灵活性差。
- 特殊字符法:以指定字符(串)为包的结束标志。可能出现错误解析,需要进行字符转义
- 协议支持(首部 + 数据体):包头大小固定,且含有包整体大小的字段
- 前提:TCP 是
- 其他问题
- TCP心跳机制
- 保证连接可靠性:TCP的心跳机制可以检测连接的存活状态,避免连接因为长时间没有数据传输而被认为已经断开。
- 发送频率灵活性:心跳包的发送频率可以根据实际情况进行调整,尽量减少网络带宽的占用
- 如何手动关闭一个TCP连接
- 伪造一个能关闭 TCP 连接的 RST 报文,其中,合法的 RST 报文必须同时满足
四元组相同
和序列号正好落在对方的滑动窗口内
- 伪造一个能关闭 TCP 连接的 RST 报文,其中,合法的 RST 报文必须同时满足
- TCP心跳机制
TCP数据传输
- 可靠传输
- 序号seq:通过首部的序号字段保证数据为有序字节流
- 确认ack:首部中的确认号是期望收到对方下一个报文段数据的第一个字节的序号
- 重传
- 超时重传:为每个发送的报文段设置
计时器
,设置的重传时间到期但未收到确认就重传该报文段 - 冗余ACK:当发送方收到同一个报文段的
3个重复ACK
,就立即重传该报文
- 超时重传:为每个发送的报文段设置
- 流量控制(注重
两端
)- 目的:在发送方和接收方之间创建一个窗口,用来控制传输数据块的数量。发送方可以根据窗口的大小来发送数据块,接收方可以根据窗口的大小来接收数据块。
- 原理:
接收方
通过确认报文中的窗口字段的接收窗口rwnd
值告知允许发送方的最大窗口值,发送方根据当前网络拥塞状估计拥塞窗口cwnd
值,实际发送窗口取两者最小的那个。
- 拥塞控制(注重
传输
)- 目的:防止过多的数据注入网络,保证全局网络不过载
- 基本概念
- 接收窗口:告诉对方自己每次最大接收的数据容量
- 拥塞窗口:发送方根据根据自己估算的网络拥塞程度而设置的窗口值
- 发送窗口上限 = min(接收窗口,拥塞窗口)
- 窗口协商:
- 第一次握手:客户端会向服务器发送SYN握手报文,其中包含客户端的初始序列号和客户端的接收窗口大小。
客户端告知服务器自己的接收窗口大小
。 - 第二次握手:服务器收到SYN报文后,会回复一个SYN-ACK报文,其中包含服务器的初始序列号和服务器的接收窗口大小。
服务器告知客户端自己的接收窗口大小
- 第三次握手:客户端收到SYN-ACK报文后,会回复一个ACK报文,其中包含客户端的确认序列号和客户端的拥塞窗口大小。
客户端告知服务器的网络拥塞情况——拥塞窗口
- 初始化发送窗口:客户端和服务器都通过对方的接收窗口和客户端估计的拥塞窗口,确认各自的发送窗口上限 = min(接收窗口,拥塞窗口)
- 通信中:每当接收方成功接收并处理了一部分数据时,它会向发送方发送一个包含更新后的接收窗口大小的ACK报文。发送方会根据接收方的ACK报文更新自己的发送窗口大小。这样可以确保发送方不会发送超过接收方可以处理的数据量,同时也可以利用网络带宽的最大化。因此,TCP通信过程中的接收窗口大小是动态变化的。
- 第一次握手:客户端会向服务器发送SYN握手报文,其中包含客户端的初始序列号和客户端的接收窗口大小。
- 拥塞控制算法
- 慢启动算法:每进行一次传输(往返时延RTT),拥塞窗口cwnd就会翻倍。直到拥塞窗口cwnd增大到的慢开始门限ssthresh(TCP默认为65535字节),再改用拥塞避免算法
- 拥塞避免算法:发送端的拥塞窗口cwnd每经过一个RTT就
线性增加
一个MSS的大小。但是当发送方未按时收到确认,重传计时器超时,则令慢开始门限ssthresh等于当前cwnd的一半
,再将拥塞窗口cwnd重新设置为1,执行慢开始算法。 - 慢开始算法和拥塞避免算法的使用是根据cwnd和ssthresh确定的
当cwnd<ssthresh时,使用慢开始算法
当cwnd>ssthresh时,使用拥塞避免算法
当cwnd=ssthresh时,既可以使用拥塞避免算法也可以使用慢开始算法 - 快重传算法:当发送方收到三个重复的ACK报文时,直接重传对方未收到的报文段,而不必等待重传计时器超时
- 快恢复算法:发送端连续收到三个冗余ACK时,则把慢开始门限ssthresh设置为出现拥塞时发送方cwnd的一半,再把cwnd设置成为ssthresh改变后的数值,执行拥塞避免算法线性增大
- 为何快速重传是选择3次ACK?
- 两次ACK可能是乱序造成的,而三次以上的ACK通常是丢包造成的,这是实践经验。
连接释放
- 四次挥手
- 前提: 通信双方处于已连接状态ESTABLISHED
- 第一次挥手:客户端发送连接释放报文,并进入终止等待状态FIN_WAIT1
seq=u,FIN=1
:告知服务器可以停止接收了,但是客户端自己仍然可以接收
- 第二次挥手:服务端收到连接释放报文段后即发出确认报文段,服务端进入CLOSE_WAIT关闭等待状态
- 服务端发送确认报文端:
序号seq=v,确认标志ACK=1,确认号ack=u+1
,表示确认客户端到服务端的连接释放 - 客户端收到服务端的确认后,进入
FIN_WAIT2终止等待2状态
,等待服务端发出的连接释放报文段。
- 服务端发送确认报文端:
- 第三次挥手:服务端发出连接释放报文段,进入LAST_ACK最后确认状态
- 连接释放报文段:
FIN=1,ACK=1,序号seq=w,确认号ack=u+1
- 连接释放报文段:
- 第四次挥手:客户端收到连接释放报文段后即发出确认报文段,客户端进入TIME_WAIT时间等待状态。然后等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
- 客户端发送确认报文段:
seq=u+1,ACK=1,ack=w+1
- 客户端发送确认报文段:
- 四次挥手的特点
- TCP是全双工的,每次挥手只能关闭FIN发送方向的数据传送链路,需要
发送方发送FIN报文
和接收方发送ACK报文
- TCP是全双工的,每次挥手只能关闭FIN发送方向的数据传送链路,需要
- 为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSE状态?
- MSL(Maximum Segment Lifetime)是指任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
- 客户端发送最后的
确认报文
不能立即关闭TCP连接,如果服务器没有收到该确认报文会超时重传FIN报文,所以TIME_WAIT状态的客户端仍然可以响应,但是客户端会设置一个计时器 - 如果客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
- 服务器出现大量close_wait的连接的原因是什么?有什么解决方法?
- close_wait状态是在TCP四次挥手的时候,收到FIN但是没有发送自己的FIN时出现,服务器出现大量close_wait状态的原因有两种:
- 服务器内部业务处理占用了过多时间,都没能处理完业务;或者还有数据需要发送;或者服务器的业务逻辑有问题,没有执行close()方法
- 服务器的父进程派生出子进程,子进程继承了socket,收到FIN的时候子进程处理但父进程没有处理该信号,导致socket的引用不为0无法回收
- 处理方法:
- 停止应用程序
- 修改程序里的bug
- close_wait状态是在TCP四次挥手的时候,收到FIN但是没有发送自己的FIN时出现,服务器出现大量close_wait状态的原因有两种: