目录
- 前言
- 1. UDP协议报文详解
- 2. TCP协议的报文格式
- 3. TCP的确认应答机制
- 4. TCP的连接管理机制
- 1. TCP三次握手的过程
- 2. TCP四次挥手的过程
- 5. 总结
前言
上一篇文章介绍了应用层中最重要的http协议,本篇文章将讲解传输层的两个协议: TCP和UDP. 由于UDP是一种简洁的协议,所以本篇文章的核心放在TCP协议上!
注: 如果对HTTP协议了解不深刻,建议先阅读这篇文章:HTTP协议详解
本章重点:
本篇文章会讲解UDP协议的报文格式, 深度解析UDP协议是怎样进行解包/封装的. 之后会讲解TCP的报文格式, 以及TCP协议中的确认应答机制,以及最重要的三次握手和四次挥手问题.
任务:
- 对于如何协议都要解决的问题: 如何分离(封装的逆过程), 如何分用?
- 理解协议的报文本身
- 详细的了解具体的报文字段及各种策略
1. UDP协议报文详解
直接上图:
1.如何将报头和有效载荷(数据)进行分离(封装)
udp采用固定长度,前8个字节就是它的报头,如何保证读到的报文是完整的呢?通过16位udp长度.
2.如何将有效载荷进行分用?
通过16位目的端口号。这个端口号就是我们上层服务绑定的端口号,根据这个端口号反向查表,找到各个进程,把有效载荷分用给各个进程。
UDP的特点和缓冲区:
注意UDP的最大数据是2^16-1. 也就是64K. 这里经常会在面试中被问到.
2. TCP协议的报文格式
直接上图:
1. 如何把报头和有效载荷(数据)进行分离?
2.如何将有效载荷进行分用?
根据目的端口。远端接收后,就能分析出报文里的目的端口,所以就能把报文交付给指定进程。
TCP报头的其他字段数据, 比如: 序号, 确认序号, 窗口大小, 6位标志位等. 就是TCP用来保证它的效率和可靠性时需要使用到的字段,下面就来边理解TCP的各种机制,边理解报头字段.
3. TCP的确认应答机制
在客户端和服务端基于TCP通信时:
以第二种方式为例,如果其中一个应答丢失了呢?
假设c发送了四个请求,但是只收到3个应答,但是对于c来说,是不知道对应那个请求的应答丢失的,所以就必须在报头中引入能够标识应答唯一性的字段:32位序号和32位确认信号。
在我们发送出去的每一个报文中,都携带有一个序号,接收方收到报文给出应答时,就会在应答中携带确认序号,原则上它是我们收到的报文序号+1。
比如3001表示: 3001之前的数据已经全部收到!
为什么协议报头里面是两个序号?
TCP是全双工的,上面的图只是单向通信的,如下图双向通信:
服务端除了给客户端应答外,也能给客户端发送数据,而双方在进行通信的时候,发送的是封装成报文(报头/报头+数据)的格式。在TCP中,如果服务器既要给应答,又要发数据,那服务器就有可能把应答数据这两个报文合并成一个报文,这叫做 捎带应答(后面会讲)。这是TCP当中提高传送效率的一个重要机制。
所以此时服务端也要维护自己的数据,这就要填32位序号,又要维护返回的应答,这就要填32位确认序号。
所以在TCP协议里面就要用2个序号。
又有问题了,在TCP通信的过程中,客户端肯定会有很多个,客户端也会发送各种类型的信息给服务端,服务端就会收到各种报文,那服务端怎么区分呢?(服务端到客户端也是如此)
为了区分报文的类型,在TCP报头中,就存在6个标记位(主要是URG,ACK,PSH,RST,SYN,FIN)(其实不止6个,这里主要讲6个)。
其中在确认应答机制中,ACK表示应答报文,只要是应答报文,ACK标记位就变为1,确认序号也要起作用。
所以在确认应答机制中,我们学习了以下几个字段:
- 32位序号
- 32位确认序号
- ACK标记位
4. TCP的连接管理机制
在这个机制中涉及TCP的三次握手和四次挥手!!
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接:
1. TCP三次握手的过程
要明确一点, 三次握手的目的是为了建立连接. 在TCP报头中的六位标记位中, 有一个标记位是SYN. 它代表请求建立连接, TCP三次握手离不开SYN标记位.
TCP三次握手的过程可以总结为: 客户端向服务器发送一个SYN标记位置1的报文. 服务器收到此报文后, 向客户端发送一个包含了ACK和SYN标记位置1的报文, 标识着服务器收到了客户端的SYN, 并且服务器也想和客户端建立连接. 客户端收到此报文后, 会再给服务端发送一个ACK, 一旦发送此ACK后, 客户端就已经建立好了连接. 同理, 服务器收到此ACK后也就建立好的连接.
可以在图中看到,connect函数发起了三次握手,connect只是要求客户端向服务端发送SYN,从此往后三次握手的具体过程,由双方的oS自动协商.服务器在listen状态时会收到客户端的SYN报文, 对应在代码中也就是使用listen函数进行监听. TCP三次握手建立连接的过程和accept函数是没有关系的, accept函数负责当连接建立好后, 将连接拿到内存当中!
这里又有两个问题:
1. 那如果最后一次客户端对服务端的ACK应答真的丢失了呢?
就要使用RST:reset 连接重置标志位.
收到该标志位的主机,要对异常连接重新释放,重新建立,重新进行三次握手.
2. 为什么是三次握手?其它次数行不行?
这个问题分为奇数次握手和偶数次握手来讲解:
1.偶数次握手
拿四次握手为例, 只要是偶数次握手, 那么最后一个ACK报文一定是服务器发送给客户端的. 此时就会有一个问题: 服务器发送了ACK之后, 就认为建立好连接了. 但是我们知道, 维护连接是有成本的, 如果我们发送回去的ACK报文, 客户端不进行处理, 但是我们又为它建立了连接, 这不是浪费资料了吗? 是的, 不仅如此, 如果使用偶数次握手, 客户端可以无脑向服务器发送SYN报文, 并且收到服务器的ACK报文后不建立连接. 这样就可以低成本的攻击一台服务器, 让它维护的连接数过大, 从而把这个服务挂掉. 而奇数次握手, 一定是客户端最后一个发送ACK, 一旦客户端发送了ACK后就会进行连接状态, 服务器会收到ACK后才会建立连接, 可以防止别人无成本攻击你的主机.
2. 偶数次握手
很明显奇数次握手肯定是比偶数次握手好的, 那么现在来分析一下, 为什么奇数次握手是三次, 不是一次也不是五次? 很明显一次握手是无法完成建立连接这个任务的. 那么五次握手行不行呢? 当然是可以的, 但是三次握手就足够建立连接, 五次, 七次也能建立连接, 但是三次是最优解.
2. TCP四次挥手的过程
四次挥手的目的是为了断开连接. 在TCP的六位标记位中有一个叫FIN的标记位, 它代表要和对端断开连接. 四次挥手离不开这个标记位.
TCP四次挥手的过程可以总结为:客户端会先给服务器发送一个带有FIN标志位置1的报文, 服务器收到此请求后会给客户端返回一个ACK, 并且会将没发完的数据发给客户端. 发完后会给客户端发送FIN,标识我的数据已经给你发完, 可以关闭连接了. 此时客户端收到此消息后给服务器发送一个ACK就断开连接了.
对TIME_WAIT状态的理解
连接断开后, 会维持一段时间的TIME_WAIT状态, 在此期间, 不能重新在同样的端口启动服务.
为什么是TIME_WAIT的时间是2MSL?
若c端发送FIN时, s端恰好也想和c端断开连接, 那么s端可以把FIN和ACK一起发送给c端. 所以四次挥手在某种情况下也是三次挥手.
为什么一般情况下是四次挥手?
使用最小的通信成本,建立了断开连接的共识。
双方都不和对方通信了,并且也知道对方也不和我通信了!
所以在连接管理机制中,我们学习了以下几个字段:
- SYN标记位
- RST标记位
- FIN标记位
5. 总结
本篇文章介绍的两种机制是TCP协议保证自身数据传输时的可靠性和效率性的.其中TCP的三次握手和四次挥手是在面试过程中网络部分考察的最高频的问题,一定要捋清楚它们的过程.