目录
UDP——用户数据报协议(User Datagram Protocol)
TCP传输控制协议 TCP(Transmission Control Protocol)
简介
在计算机网络中,网络层把分组发送到目的主机,但真正通信的并不是主机,而是主机中的进程。
传输层提供了进程间的逻辑通信,向高层用户屏蔽了下面网络层的细节,使得应用程序看起来像是有一条端到端的逻辑通信信道。
而TCP/UDP协议是传输层最重要的两种协议,提供了应用程序间的通信,它负责数据从发送端到接收端的信息流格式化和保证可靠传输。
UDP——用户数据报协议(User Datagram Protocol)
特点
-
面向无连接
不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据直接可以开始发送
-
不可靠性
体现在面向无连接的特性上,通信不需要建立连接,想发就发,尽最大可能交付
没有确认和重传机制,如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息
-
面向报文段
不会对数据报文进行任何拆分和拼接操作,保留报文的边界,只是添加UDP首部,具体来说
在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,没有拼接操作
在发送端,应用层将数据传递给传输层,UDP 只会给数据增加一个 UDP 首部,然后传递给网络层
如果发送端调用一次sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节,而不能循环调用10次recvfrom,每次接收10个字节
所以UDP不能够灵活的控制读写数据的次数和数量
-
单播,多播,广播
UDP 支持一对一、一对多、多对多、多对一的方式
-
头部开销小
UDP头部大小8字节,TCP头部大小最小为20字节,最大为60字节,因此UDP传输开销比TCP小,传输数据报文时高效
UDP首部格式
UDP首部只有8个字节,包括源端口、目的端口、报文长度、校验和,12字节的伪首部用于计算检验(只检验,不恢复)时临时添加
TCP传输控制协议 TCP(Transmission Control Protocol)
特点
-
面向连接
通过三次握手与四次挥手实现连接的建立与释放一对一连接
-
可靠传输
采取以上多种方式来保证TCP的可靠性传输
-
面向字节流
TCP不像UDP一样那样一个个报文独立地传输,而是在不保留报文边界的情况下以字节流方式进行传输
-
点对点通信
每条TCP连接只能是一对一通信
-
全双工通信
通信双方都有发送缓存和接收缓存,应用进程在任意时刻都可以收发数据
TCP首部格式
端口号
一个TCP连接由四个要素唯一确定:(源IP,源端口号)+ (目地IP,目的端口号)
其中IP地址由上一层IP协议负责传递,源端口号和目地端口各占16位=2字节,也就是端口的范围是2^16=65535
从1024-65535是用户使用的端口范围,另外1024由系统保留
序号
TCP通信过程中某一个传输方向上的字节流的每个字节的序号,通过seq来确认发送的数据有序。如:现在序列号为1000,发送了1000字节,下一个序列号就是2000
确认号
用来响应TCP报文段,对上一次seq序号做出确认,给收到的TCP报文段的序号seq+1
头部长度
标识TCP首部有多少个4字节,4位1111最大表示15,则TCP首部最长为60字节,最短只有前20字节,不带选项
保留位
6位保留供以后使用
标志位
ACK 标识确认号是否有效(确认报文段)
RST 标识要求对方重新建立连接(复位报文段)
SYN 标识请求建立一个连接(同步报文段)
FIN 标识通知对方本端连接即将关闭(结束报文段)
URG 标识紧急指针是否有效
PSH 提示接收端应用程序应该立即从TCP接收缓冲区读取数据,为接收后续数据腾出空间
窗口大小
流量控制的手段,告诉对方本端TCP接收缓冲区还能容纳多少数据,控制对方发送数据的速度
校验和
由发送端填充,接收端对TCP报文段执行CRC校验检验报文是否损坏(包括首部与数据)
紧急指针
在URG=1时才有意义,它和序号字段的值相加,表示最后一个紧急数据的下一字节的序号,用于发送端向接收端发送紧急数据
选项(最多40字节)
连接过程(三次握手)
-
发生条件
socket编程中,客户端connect时触发三次握手
-
握手过程
1.第一次握手,客户端主动向服务器发送连接请求
客户端随机生成一个起始序列号ISN(比如是100),并将SYN标志位置1,指明客户端打算连接的服务器的端口,并将该数据包发送给服务器端
2.第二次握手,服务端向客户端发送确认连接请求报文
服务端收到客户端发过来的报文后,发现SYN=1,知道这是一个连接请求,将客户端的起始序列号100保存,并且随机生成一个服务端的起始序列号(比如是300)
然后给客户端回复一段报文,回复报文包含SYN和ACK标志(也就是SYN=1,ACK=1)、序列号seq=300、确认号ack=101(客户端发过来的序列号+1)
3.第三次握手,客户端再次向服务端发送连接确认
客户端收到服务端的回复后发现ACK=1并且ack=101,于是知道服务端收到了序列号为100的那段报文,同时发现SYN=1,知道了服务端同意了这次连接,于是就将服务端的序列号300保存
客户端再回复一段报文给服务端,报文包含ACK标志位(ACK=1)、ack=301(服务端序列号+1)、seq=101(第一次握手时发送报文是占据一个序列号的,所以这次seq就从101开始
需要注意的是不携带数据的ACK报文是不占据序列号的,所以后面第一次正式发送数据时seq还是101
当服务端收到报文后发现ACK=1并且ack=301,就知道客户端收到序列号为300的报文了,就这样客户端和服务端通过TCP建立了连接,可以调用accept函数获取此连接
-
常见问题
1.为什么要三次握手?可以是两次吗?
答:为了防止客户端已失效的连接请求到达服务器,让服务器错误地打开连接。
解释:客户端发送的连接请求如果在网络中滞留,就会隔很长一段时间才能收到服务器端发送回的连接确认,客户端等待一个超时重传时间后,会请求重新连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有三次握手,客户端会忽略之前发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会打开连接
2.什么是半连接队列和全连接队列?
半连接队列:服务器第一次接到客户端的SYN请求后,将双方未完全建立连接的请求放在半连接队列中
全连接队列:已经完成三次握手,建立起的连接会放在全连接队列中,队列已满则会产生丢包现象
3.第一次、第二次握手可以携带数据吗?为什么?
答:RFC标准规定,SYN位为1的报文段不允许携带数据
解释:前两次握手时,连接并没有真正建立,假如可以携带数据,如果有人恶意攻击服务器,在SYN报文中放入大量数据,服务器会消耗大量资源去处理
4.第三次握手可以携带数据吗?为什么?
答:RFC标准有说明允许第三次握手携带数据
解释:第三次握手时,客户端和服务器已经建立连接,具备正常的收发能力
5.SYN攻击是什么?如何解决?
答:客户端通过配合伪IP向服务器发送大量的半连接请求,当服务器未收到客户端的第三次握手确认时,重发确认请求包,一直超时
由于源地址本身不存在,服务器需要不断重发到超时,伪造的请求SYN包将长时间占用半连接队列,占用服务器资源,导致正常SYN请求因为队列满而被丢弃,引起网络拥塞甚至瘫痪
解决:缩短超时重传时间,增加最大半连接数、过滤网关防护、SYN cookies技术
6.序列号有什么作用?为什么要随机初始化序列号?
答:用来跟踪该端发送的数据量,比如初始序列号是100,发送200字节数据后,下一个数据号变为300,也能用于对重复发送的数据包进行区分
解释:https://blog.csdn.net/wangquan1992/article/details/97900840
-
功能总结
确认双方收发能力是否正常,指定各自的初始序列号,并同步双方的序列号和确认号,交换TCP窗口大小信息,为后面建立TCP连接,进行可靠传输准备
释放过程(四次挥手)
-
挥手过程
情景
客户端初始化的序列号ISN=100,服务端初始化的序列号ISN=300。TCP连接成功后客户端总共发送了1000个字节的数据,服务端在客户端发FIN报文前总共回复了2000个字节的数据
1.第一次挥手,客户端要向服务端发送连接释放报文
无论数据传输是否完成,都可以发送释放连接报文并停止发送数据
释放连接报文包含FIN标志位(FIN=1)、序列号seq=1101(100+1+1000,其中的1是建立连接时占的一个序列号)
此时客户端处于FIN-WAIT-1状态
需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号
2.第二次挥手,服务端向客户端发送回复确认报文
服务端收到客户端发的FIN报文后给客户端回复确认报文
确认报文包含ACK标志位(ACK=1)、确认号ack=1102(客户端FIN报文序列号seq=1101+1)、序列号seq=2300(300+2000)
此时服务器处于CLOSE-WAIT状态,客户端处于FIN-WAIT-2状态
服务器不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完
3.第三次挥手,服务端向客户端发送连接释放报文
服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文
报文包含FIN和ACK标志位(FIN=1,ACK=1)、确认号和第二次挥手一样ack=1102、序列号seq=2350(2300+50)
此时服务器处于LAST-ACK状态
4.第四次挥手,客户端向服务端发送回复确认报文
客户端收到服务端发的FIN报文后,向服务端发出确认报文
确认报文包含ACK标志位(ACK=1)、确认号ack=2351、序列号seq=1102
此时客户端处于TIME-WAIT状态
注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(Maximum Segment Life最长报文段寿命的2倍时长)后才释放TCP连接。
而服务端一旦收到客户端发出的确认报文就会立马释放TCP连接,变为CLOSED状态,所以服务端结束TCP连接的时间要比客户端早一些
-
常见问题
1.为什么TCP连接的是时候是3次,关闭时却是4次?
答:因为TCP连接是全双工通信,即客户端、服务器都可以收发数据,则在断开连接时,需要服务器和客户端都确定对方不再发送数据
解释:
第一次挥手,客户端向服务器发送,服务器得知客户端将不再发送数据
第二次挥手,服务器向客户端发送,客户端得知服务器已经知道客户端不再发送数据
第三次挥手,服务器向客户端发送,客户端得知服务器将不再发送数据
第四次挥手,客户端向服务器发送,服务器得知客户端已经知道服务器不再发送数据
第一、二次挥手时,服务器可能还在向客户端发送数据,所以第二次挥手和第三次挥手不能合并
2.什么是2MSL?为什么客户端发出第四次挥手的确认报文后,客户端要等2MSL时间才能释放TCP连接?
答:2MSL是报文段最长寿命时长的2倍,即报文段在TCP连接中可以存活的最长时间
一个确认报文(第四次挥手)+一个请求报文(第三次挥手)
解释:
客户端发送出ACK=1报文后,开始维护2MSL计时器,假设客户端最后发送ACK=1确认收到服务器关闭连接的报文,在网络中滞留没有到达服务器,服务器在一段时间没有收到客户端的ACK=1回复,会重发第三次挥手的连接释放报文。客户端接到此报文,重置2MSL计时器,再次向服务器发送ACK=1报文,如果直到2MSL计时器到时间都没有收到服务器重发的连接释放报文,则客户端释放TCP连接
如果此时客户端已经释放了TCP连接,没有等待2MSL的时间,则不能接到服务器重发的连接释放报文,重发的报文找不到对应的连接,如果再向服务器发送TCP连接请求,此时无法保证两次连接的端口号不同,则可能出现这样的问题:前一次的连接某些数据滞留在网络中,这些延迟数据在建立新连接后到达客户t端,由于新老连接的端口号和IP都一样,TCP协议就认为延迟数据是属于新连接的,新连接就会接收到脏数据,这样就会导致数据包混乱
3.如果已经建立连接,但客户端出现故障了怎么办?
答:采用心跳机制保活
解释:服务器维护一个计时器,每次服务器接到客户端的请求,都会重新复位计时器,如果超过一定时间没有接收到客户端的数据,服务器会向客户端发送一个探测报文,每隔一定时间发送一次,若连续发送一定数量的探测报文没有回应的话,服务器就认为客户端出了故障,关闭连接
-
功能总结
确保数据传输完成
超时重传
-
什么是超时重传?
主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B
如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发
等待一段时间仍未收到B的确认包,进行第二次重传(每次等待的时间一般是指数增长,列如间隔时间为1s,2s,4s,8s…)
如果这个过程发生在建立三次握手连接时,重传的次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除
流量控制
-
什么是流量控制?
所谓流量控制,就是如果接收端和发送端对数据包的处理速度不同,如何让双方达成一致,让发送方的发送速率不要太快,让接收方来得及接收
-
控制手段——滑动窗口
TCP会话的双方都各自维护一个发送窗口和一个接收窗口,它是缓存的一部分,用来暂时存放字节流,发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收
要弄清什么是滑动窗口,首先要理解TCP缓存中的数据分类
发送方的发送缓存数据分为4类:
1. 已发送,已收到ACK
2. 已发送,未收到ACK
3. 未发送,但允许发送
4. 未发送,但不允许发送
其中类型2、3属于发送窗口
接收方的接收缓存数据分为3类:
1. 已接收
2. 未接收但准备接收
3. 未接收而且不准备接收
其中类型2属于接收窗口
接收窗口大小代表了设备一次能处理多少数据,之后再传给应用层
接收方通过 TCP 报文段中的窗口大小字段,告诉发送方自己的接收窗口大小,发送方根据这个值和其它信息设置自己的发送窗口大小
滑动机制
1.发送窗口只有收到发送窗口内字节的ACK确认,才会移动发送窗口的左边界
2.接收窗口只会对窗口内最后一个按序到达的字节进行确认,当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认,以此确保对端会对这些数据重传
如下图:接收窗口例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {32,33} 没有按序收到,因此只对字节 31 进行确认
发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收
详见:https://blog.csdn.net/yao5hed/article/details/81046945
-
功能
流量控制解决了两台主机之间因传送速率而可能引起的丢包问题,在一方面保证了TCP数据传送的可靠性
然而如果网络非常拥堵,此时再发送数据就会加重网络负担,那么发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题
拥塞控制
-
什么是网络拥塞?
网络拥塞(network congestion)是指在分组交换网络中传送分组的数目太多时,由于存储转发节点的资源有限而造成网络传输性能下降的情况
-
拥塞控制算法——慢启动、拥塞避免、快重传和快恢复
假设网络中有两台主机A、B已建立TCP连接,A向B发送数据,通过拥塞窗口cwnd来控制每次发送数据包的个数
慢启动
A开始时不会发送大量的数据,而是先探测一下网络的拥塞程度
由小到大逐渐增加拥塞窗口的大小,在没有出现丢包时每收到一个ACK就将拥塞窗口大小+1
向B发送的数据包个数为cwnd = 1,2,4,8,16……即每轮次发送窗口增加一倍,呈指数增长
当达到慢启动阈值ssthresh (假设为16)时,A转而使用拥塞避免算法
拥塞避免
在拥塞避免阶段,每轮次发送窗口加一,呈线性增长
发送数据包个数为cwnd = 16,17,18……24
拥塞窗口肯定不会无限增长,网络传输中必然出现超时事件
假设拥塞窗口大小达到cwnd = 24时,24个报文段在传输过程中丢失4个,B只收到20个报文段,给A依次回复20个确认报文段
一段时间后,丢失的4个报文段的重传计时器超时了,A判断可能出现拥塞
A将慢启动阈值ssthresh设为24/2 = 12,并重新启动慢开始算法,cwnd从1开始指数增长
快重传
假设在传输过程中,A给B发送1,2,3,4,5……N个数据包,如果B收到了1,2,4……却始终没有收到3
这个时候就会重复确认2,意在告诉A,3还没收到,可能已经在网络中丢失
当A连续收到了三个确认2的ACK,且3的超时事件还没发生。A就知道3可能丢失了,这个时候A就不必等待3设置的计时器到期了,而是快速重传3
快恢复
快恢复配合快重传使用,把阈值ssthresh设置为N的一半,即ssthresh = N/2
但是此时并非把拥塞窗口cwnd重新设置为1,而是让cwnd = ssthresh,重新开始拥塞避免算法的线性增长
详见:
https://zhuanlan.zhihu.com/p/97709686
https://blog.csdn.net/m0_37962600/article/details/79993310
-
功能
防止过多的数据注入到网络当中,这样可以使网络中的路由器或链路不致过载
总结
UDP和TCP的对比
UDP数据包与TCP字节流的区别
发送端每执行一次sendto(),UDP就将其封装成数据报发送,接收端必须及时针对每一个UDP数据报执行recvfrom(),否则将会丢包
发送端执行send时,TCP先将数据放入TCP发送缓冲区中,当TCP真正开始发送数据后,将数据封装成一个或多个TCP报文段发出,接收端接到一个或多个报文段后,TCP模块将数据按TCP报文段的序号依次放入TCP接收缓冲区,通知接收端recv()读取数据,接收端可以一次性读,也可以分次读,取决于读缓冲区大小