UDP
面向报文
UDP 是一个面向报文(报文可以理解为一段段的数据)的协议。意思是 UDP 只是报文的搬运工,不会对报文进行任何拆分和拼接操作。 具体来说
- 在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了
- 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会有任何拼接操作
不可靠性
- UDP 是无连接的,也就是说通信不需要建立和断开连接
- UDP 也是不可靠的,协议收到什么数据就传递什么数据,并且也不会备份数据,对方能不能收到是不关心的
- UDP 没有拥塞控制,一直会以恒定的速度发送数据,即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是TCP
高效
因为 UDP 没有 TCP 那么复杂,需要保证数据不丢失且有序到达,所以 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的。
UDP 头部包含了以下几个数据:
- 两个十六位的端口号,分别为源端口(可选字段)和目标端口
- 整个数据报文的长度
- 整个数据报文的检验和(IPV4 可选字段),该字段用于发现头部信息和数据中的错误
传输方式
UDP不止支持一对一的传输方式,同样支持一对多、多对多、多对一的方式,也就是说 UDP 提供了单播、多播、广播的功能
TCP
头部
TCP 头部比 UDP 头部复杂的多
对于 TCP 头部来说,以下几个字段是很重要的
- Sequence number(序号),这个序号保证了 TCP 传输的报文都是有序的,对端可以通过序号顺序拼接报文
- Acknowledgement number(确认号),这个序号表示数据接收端期望接收的下一个字节的编号是多少,同时也表示上一个序号的数据已经收到
- Window Size(窗口大小),标识还能接收多少字节的数据,用于流量控制
- 标识符
- URG=1:该字段表示本数据包的数据部分包含紧急信息,是一个高优先级数据报文,此时紧急指针有效。紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部
- ACK=1:该字段表示确认号字段有效,此外,TCP 还规定在连接建立后传送的所有报文字段都必须把 ACK 置为一
- PSH=1:该字段表示接收端应该立即将数据push给应用层,而不是等到缓冲区满后再提交
- RST=1:该字段表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文和拒绝连接请求
- SYN=1:当 SYN=1,ACK=0 时,表示当前报文段是一个连接请求报文;当 SYN=1,ACK=1 时,表示当前报文段是一个同意建立连接的应答报文
- FIN=1:该字段表示此报文段是一个释放连接的请求报文
建立链接三次握手
在 TCP 协议中,主动发起请求的一端为客户端,被动连接的一端为服务端。不管是客户端还是服务端,TCP 连接建立完后都能发送和接收数据,所以 TCP 也是一个全双工的协议。
起初,两端都为 CLOSED 状态,在通信开始前,双方都会创建 TCB。服务器创建完 TCB 后进入 LISTEN 状态,此时开始等待客户端发送数据。
- 第一次握手:客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态,x 表示客户端的数据通信初始序号
- 第二次握手:服务端收到连接请求报文后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态
- 第三次握手:当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功
PS:第三次握手可以包含数据,通过 TCP 快速打开(TFO)技术。起始只要涉及到握手的协议,都可以使用类似 TFO 的方式,客户端和服务端存储相同 cookie ,下次握手时发出 cookie 达到减少 RTT 的目的。
明明两次握手就可以建立起连接,为什么还需要第三次应答?
因为这是为了防止失效的连接请求报文段被服务端接收,从而产生错误。举个例子:
客户端发送了一个连接请求A,但是因为网络原因造成了超时,这是 TCP 会启动超时重传的机制再次发送一个连接请求B。此时请求顺利到达服务端,服务端应答完就建立了请求。如果连接请求A在两端关闭后终于抵达了服务端,那么这时服务端会认为客户端又需要建立 TCP 连接,从而应答了该请求并进入 ESTABLISHED 状态。此时客户端其实是 CLOSED 状态,那么就会导致服务端一直等待,造成资源浪费。 (在建立连接中,任意一端掉线,TCP 都会重发 SYN 包,一般会重试五次,在建立连接中可能会遇到 SYN FLOOD 攻击。遇到这种情况可以选择调低重试次数或者干脆在不能处理的情况下拒绝请求)
断开链接四次握手
TCP是全双工的,在断开连接时两端都需要发送 FIN 和 ACK
- 第一次握手:若客户端认为数据发送完成,则它需要向服务端发送连接释放请求
- 第二次握手:服务端收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后回发送 ACK 包,并进入 CLOSE-WAIT 状态,表示客户端到服务端的连接已经释放,不接收客户端发的数据了。但是因为 TCP 连接是双向的,所以服务端仍旧可以发送数据给客户端
- 第三次握手:服务端如果此时还有未发完的数据则会继续发送,发送完后会向客户端发送连接释放请求,然后服务端便进入 LAST-ACK 状态。
通过延迟确认的技术(通常有时间限制,否则对方会误认为需要重传),可以将第二次和第三次握手合并,延迟 ACK 包的发送
- 第四次握手:客户端收到释放请求后,向服务端发送确认应答,此时客户端进入 TIME-WAIT 状态。该状态会持续2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃),若该时间段内没有服务端的重发请求,就进入 CLOSED 状态。当服务端收到确认应答后,也进入 CLOSED 状态
为什么客户端要进入TIME-WAIT 状态,等待 2MSL 时间后才进入 CLOSED 状态?
为了保证服务端能收到客户端的确认应答。若客户端发完确认应答后直接进入 CLOSED 状态,如果确认应答因为网络问题一直没有到达,那么会造成服务端不能正常关闭。
常见面试题
为什么连接的时候是三次握手,关闭的时候却是四次握手?
- 关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据,但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方,再发送 FIN 报文给对方来表示同意现在关闭连接
- 因此,ACK 和 FIN 一般都会分开发送,从而导致多了一次
为什么不能用两次握手进行连接?
- 若建立连接只需要两次握手,客户端并没有太大变化,任然需要获得服务端的应答后才进入 ESTABLISHED 状态,而服务端在收到连接请求后就进入 ESTABLISHED 状态。
- 此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确认并确认应答,双方便开始通信,通信结束后释放连接。此时如果失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入 ESTABLISHED 状态,等待发送数据或主动发送数据
- 但此时客户端早已进入 CLOSED 状态马,服务端会一直等待,浪费服务端连接资源
如果已经建立了连接,但是客户端突然出现故障了怎么办?
- TCP保活计时器每次客户端请求服务器会重置计时器,当2小时之内没收到客户端任何数据时,会每隔75s向客户端发一个探测报文,若接连发送10个,客户端都没有反应,则认为客户端故障,关闭连接。
什么是SYN洪泛攻击?
- SYN洪泛攻击就是利用TCP协议的特性(三次握手)。攻击者发送TCP的 SYN,SYN是TCP三次握手中第一个数据包,而当服务器返回ACK后,该攻击者就不对其进行再确认,那这个TCP连接就处于挂起状态,也就是半连接状态,服务器收不到再确认的话,还会重复发送ACK给攻击者。
- 这样更加会浪费服务器资源。攻击者就对服务器发送大量的这种TCP连接,由于每一个连接都无法完成三次握手,所以就在服务器上,这些TCP连接会因为挂起状态而消耗CPU和内存,最后服务器可能死机。
推荐文章