# 【NO.287】Linux网络编程-UDP和TCP协议详解

## 1.引言

> 网络协议是每个程序员都要掌握的基础知识,干啥都离不开网络,就算在家里新买了个路由器不是吗,同事连不上网,你的女朋友手机没有网看剧了正看到高潮部分,到那时候你打开百度……那嫌弃的你的眼神仿佛在说,就这?程序员连个网都不会修?以上都是臆想,以实际为准.虽然看完本文章,你还是需要去百度怎么修复网络问题,但是你已经知道为什么会出现这种问题了!

## 2. UDP

UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议.在OSI中,第四层传输层,处于IP协议的上一层UDP有不提供数据包分组,组装和不能对数据包进行排序的缺点,也就是说,当报文发送后,无法监控其是否完整安全到达的,就想一个坏掉了的水龙头,你不论怎么让他停止他都只会输出,也不管你的桶满没满,就像爱一样

### 2.1.特点

#### 2.1.1. 面向无连接:

首先UDP是不需要和TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了.并且也只是主句报文的搬运工,不会对数据报文进行任何拆分和拼接操作

**具体来说:**

- 在发送端,应用层将数据传递给UDP协议,UDP只会给数据增加一个UDP头标识下UDP,然后就传递给网络层了
- 在接收端,网络层将数据传递给传输层,UDP只去除IP报头就传递给应用层,不会任何拼接操作

#### 2.2.2. 有单播,多播,广播的功能

UDP不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说UDP提供了单播,多播和广播的功能.

#### 2.2.3. UDP是面向报文的

发送方的UDP读应用程序交下来的报文,在添加首部后就向下交付IP层.UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界.因此,应用程序必须选择合适大小的报文

#### 2.2.4. 不可靠性

首先不可靠性体现在无连接上,通信不需要建立连接,想发就发,这样的情况肯定不可靠

并且受到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了

再者网络环境时好时坏,但是UDP因为没有拥塞控制,一直会以恒定的速度发送数据,即使网络条件不好,也不会对发送数据进行调整.这样实现的弊端就是在网络条件不好的情况下会导致丢包,但是有点也很明显,比如电话会议等等最好就是UDP

#### 2.2.5. 头部开销小,传输数据报文是很高效的.

![img](https://pic4.zhimg.com/80/v2-fb3c7e36466e526b5fcecd40ab83fe07_720w.webp)

因此UDP的头部开销小,只有八字节,相比TCP的至少二十字节要少得多,在传输数据报文时是很高效的

## 3. TCP/IP网络模型

计算机网络设备相互通信,双方必须基于相同的方法.比如如何探测到通信目标,由那一边先发起通信,使用哪种语言进行通信,怎样结束通信等规则都需要实现确定.不同硬件,操作系统之间的通信,所有的这一切都需要一种规则,而我们吧这种规则成为协议.

**TCP/IP**是互联网相关的各类协议的总称,比如:**TCP，UDP，IP，FTP，HTTP，ICMP，SMTP **等都属于 TCP/IP 族内的协议

大家一定知道OSI七层模型,**TCP/IP**概念模型是这样的

![img](https://pic1.zhimg.com/80/v2-a267695371b6b140c23b2a4eedc42b0c_720w.webp)

- 应用层:负责向用户提供应用程序.比如HTTP,FTP,Telnet,DNS,SMTP等
- 传输层:负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文
- 网络层:负责路由以及把分组报文发送给目标网络或主机
- 链路层:负责封装和接缝IP报文,发送和接收ARP/RARP报文等

## 4.TCP

当一台计算机想要与另一台计算机通讯时,两台计算机之间的通信需要畅通且可靠,这样才能保证正确收发数据.例如当你收文件时候不希望收到的是一个损坏的文件,发过来的小电影是无法放映的,或者直接变成马赛克,当然也可能本来就有马赛克,这不是我们希望得到的,于是就用到了TCP
TCP协议全称是传输控制协议,,这是一种面向连接的,可靠的,基于字节流的传输层通信协议

### 4.1.TCP的连接过程

![img](https://pic3.zhimg.com/80/v2-7fc134a072a49086ff8e86325bccc2b2_720w.webp)

TCP报文也分为首部和数据两部分,首部默认情况下一般是20字节长度,但在20字节长度,但在一些请求情况下,会使用”可选字段”,这时,首部长度会有所增加,但最长不超过60字节

**源端口 - 16bit**
　　来源处的端口号；端口号有65536个，即$2^{16}$。
**目的端口 - 16bit**
　　目的处的端口号
**序号 - 32bit**
　　TCP在对数据进行分段的时候，会给每一个TCP报文段添加一个序号，序号字段的值其实是该文段所发送的数据的第一个字节的序号。这么做的原因是，TCP是面向连接的可靠服务，这个序号可以保证数据在传输过程中保持有序性，接受端可以通过这个序号确认收到的数据的完整性和先后顺序；
**确认号 - 32bit**
　　确认号，是期望收到对方的下一个报文段的数据的第一个字节的序号；
**数据偏移 - 4bit**
　　其实它本质上就是“首部长度”，因为“数据偏移”是指TCP报文段的数据部分的起始处距离TCP报文段的起始处的距离。（仍然很拗口，但相信你能明白）。
　　数据偏移总共占4bit，因此最大能表示的数值为15。但TCP的报文头部至少为20字节。因此数据偏移的单位是“4字节”，此处的设计和IP数据报的设计是完全相同的，所以说TCP报文段首部的长度最长为15×4=60字节，且首部长度必须为4字节的整数倍。
**保留字段 - 6bit**
　　IETF文档指出，这6bit在标准中是保留字段，留待以后使用，必须为0。我猜测，有两个目的，第一个是预留除URG/ACK/PSH/RST/SYN/FIN/之外的冗余功能位；第二个是为了对齐字节位。
**控制位 - 6bit**
又称为TCP flag，该字段从左到右分为以下六个字段，指明包的类型。同时用于控制TCP的状态机，同时ACK和SYN与三次握手协议有关，FIN与四次挥手协议有关。
　　**① 紧急字段URG - 1bit**
　　　　当URG=1时，此字段告诉系统此报文段中有紧急数据，应尽快传送。
　　**② 确认字段ACK - 1bit**
　　　　当ACK=1时，表示确认，且确认号有效；当ACK=0时，确认号字段无效。
　　**③ 推送字段PSH - 1bit**
　　　　当PSH=1时，则报文段会被尽快地交付给目的方，不会对这样的报文段使用缓存策略。
　　**④ 复位字段RST - 1bit**
　　　　当RST为1时，表明TCP连接中出现了严重的差错，必须释放连接，然后再重新建立连接。
　　**⑤ 同步字段SYN - 1bit**
　　　　当SYN=1时，表示发起一个连接请求。
　　**⑥ 终止字段FIN - 1bit**
　　　　用来释放连接。当FIN=1时，表明此报文段的发送端的数据已发送完成，并要求释放连接。
**窗口字段 - 16bit**
　　此字段用来控制对方发送的数据量，单位为字节。
　　一般TCP连接的其中一端会根据自身的缓存空间大小来确定自己的接收窗口大小，然后告知另一端以确定另一端的发送窗口大小。该字段与TCP的流量控制服务有关。
**校验和字段 - 16bit**
　　与IP协议的检验和不同，TCP的这个校验和是针对首部和数据两部分的。
**紧急指针字段 - 16bit**
　　紧急指针指出在本报文段中的紧急数据的最后一个字节的序号。

### 4.2.三次握手四次挥手(这词都听吐了,换个叫法:一键三连,取消三连加取关/滑稽))

还有一点其实人家是Three-way handshake,三步握手四步挥手的,谁握手握三次,挥手挥四次的,/吃瓜
**TIP:注意箭头的指向,结合文字进行理解,箭头没有问题**

![img](https://pic3.zhimg.com/80/v2-8d376a864a363e7c99f41d556270a706_720w.webp)

## 5. 一键三连

在三连之前服务器和客户端都为CLOSED状态.通信开始前,双方都得创建各自的传输控制块(TCB)
服务器创建完TCB后便进入LISTEN状态此时准备接受客户端发来的连接请求

### 5.1.点赞(第一次握手)

客户端向服务端发送连接请求报文段.该保温段的头部中SYN=1,ACK=0,seq=X.请求发送后客户端便进入SYN-SENT状态

- SYN = 1,ACK = 0表示该报文段位连接请求报文
- x为本次TCP通信的字节流的初始序号

**TIP:**TCP规定SYN = 1的报文段不能有数据部分,但要消耗一个序号

### 5.2.投币(第二次握手)

服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN = 1,ACK = 1,seq = y,ack = x + 1
该应答发送完成后便进入SYN-RCVD状态.

- SYN=1，ACK=1表示该报文段为连接同意的应答报文
- seq=y表示服务端作为发送者时，发送字节流的初始序号
- ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节

### 5.3.收藏(第三次握手)

当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示服务端发来的连接同意应答已经收到
该报文头部为:ACK = 1, seq = x + 1, ack = y + 1.
客户端发完这个报文后便进入ESTABLSHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成

## 6. 取消三连加取关

TCP连接的释放一共需要四部,因为TCP连接时双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向连接

### 6.1.取消点赞(第一次挥手)

若客户端认为数据发送完成,则它需要向服务端发送连接释放请求.该请求只有报文头,头中携带的主要参数为:
FIN = 1,seq = u此时,客户端将进入FIN-WAIT-1状态

- FIN = 1表示该保温是一个连接释放请求
- seq = u,u - 1是A向B发送的最后一个字节的序号

### 6.2.取消投币(第二次挥手)

服务端收到连接释放请求后,会通知相应的应用程序,告诉它客户端向服务端这个方向的连接已经释放.此时服务端进入CLOSE-WAIT状态,并向客户端发送连接释放的回答,其报文头包含:
ACK = 1,seq = v, ack = u + 1

- ACK=1：除TCP连接请求报文段以外，TCP通信过程中所有数据报的ACK都为1，表示应答。
- seq=v，v-1是B向A发送的最后一个字节的序号。
- ack=u+1表示希望收到从第u+1个字节开始的报文段，并且已经成功接收了前u个字节

客户端收到应答进入FIN-WAIT-2状态,等待服务端发送连接释放请求
第二次挥手完成后,客户端到服务端方向的连接已经释放,服务端不会接受数据,客户端也不会发送数据,但服务端到客户端的连接仍然存在,服务端可以继续向客户端发送数据

### 6.3.取消收藏(第三次挥手)

当服务端发送完所有数据后,向客户端发送连接释放请求,请求头:FIN = 1,ACK, seq = w,ack = u+1.服务端便进入LAST-ACK状态.

### 6.4.取消关注(第四次挥手)

当客户端收到释放请求后向B发送确认应答,此时客户端进入TiME-WAIT状态,该状态会持续2MSL(2分钟)时间,若该时间没有服务端重发请求的话,就进入CLOSED状态,撤销TCB.当服务端收到确认应答后,也进入CLOSED状态撤销TCB

原文链接：https://zhuanlan.zhihu.com/p/378181872

作者：[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10)