TCP&UDP协议

TCP协议

TCP是面向连接的、可靠的、基于字节流传输层通信协议,且TCP连接是全双工的。TCP会将数据临时存储到连接的发送缓存send buffer中,该send buffer是三次握手之间设置的缓存之⼀,然后TCP在合适的时间将发送缓存中的数据发送到目标主机的接收缓存中。

  • 面向连接:一定是一对一才能连接,不能像UDP协议一对多即可一个主机同时向多个主机发送消息

  • 可靠的:无论网络链路中出现怎样的链路变化,TCP都可保证一个报文一定能够到达接收端

  • 字节流消息没有边界,无论消息有多大都可进行传输,且消息是有序的,当前一个消息没有收到时,即使它先收到后面的字节,也不能扔给应用层去处理,同时对重复的报文会自动丢弃

主机之间的发送是以报文段Segment进行的,TCP会将要传输的数据流分为多个Chunk,然后向每个Chunk块中添加TCP标头,这样就形成了⼀个TCP段即报文段

每⼀个报文段可传输的长度是有限的,不能超过最大数据长度Maximum Segment SizeMSS,在报文段向下传输过程中会经过链路层数据链路层上所能通过最大数据包大小Maximum Transmission Unit最大传输单元MTU,最大传输单元通常与通信接口有关

MSS和MTU区别

计算机网络是分层的,不同层的称呼不⼀样,对于传输层来说称为报文段,而对网络层来说叫做IP数据包,故MTU可认为是网络层能够传输的最大IP数据包,而MSS可认为是传输层的概念,即TCP数据包每次能够传输的最大量

TCP头格式

TCP头格式

TCP各种功能和特点都是通过TCP报⽂结构来体现的

  • 序列号:用来解决网络包乱序问题,在建立连接时由计算机生成的随机数作为其初始值,通过SYN传给接收端主机,每发送一次数据就累加一次该数据字节数大小
  • 确认应答号用于解决不丢包问题,下一次期望收到的数据的序列号,发送端收到该确认应答以后可认为在该序号以前的数据都已经被正常接收
  • 控制位:
    • ACK:该位为 1确认应答字段变为有效,TCP规定除了最初建立连接时的SYN包之外该位必须设置为1
    • RST:该位为1时,表示TCP连接中出现异常必须强制断开连接
    • SYC:该位为1时,表示希望建立连并在其序列号字段进行序列号初始值的设定
    • FIN:该位为1时,表示之后不会再有数据发送希望断开连接当通信结束希望断开连接时,通信双方的主机之间就可相互交换FIN位置为1的TCP段

TCP连接建立

TCP是面向连接的协议,故使用TCP前必须先建立连接,而建立连接是通过三次握手而进行的

三次握手过程状态变更

一开始客户端服务端都处于CLOSED状态,先是服务端主动监听某个端口处于LISTEN状态,客户端随机初始化序号client_isn,将此序号置于TCP首部的序号字段中,同时把SYN标志位置为 1表示SYN报文,接着把第一个SYN报文发送给服务端,表示向服务端发起连接该报文不包含应用层数据,之后客户端处于SYN-SENT状态。

三次握手第一个报文-SYN报文

服务端收到客户端的SYN报文后,首先服务端也随机初始化自己的序号server_isn,将此序号填入TCP首部的序号字段中,其次把TCP首部的确认应答号字段填入client_isn + 1,接着把SYNACK标志位置为1,最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于SYN-RCVD状态

三次握手第二个报文-SYN+ACK报文

客户端收到服务端报文后还要向服务端回应最后一个应答报文,首先该应答报文TCP首部ACK标志位置为1 ,其次确认应答号字段填入server_isn + 1,最后把报文发送给服务端,该报文可携带客户到服务器的数据,之后客户端处于ESTABLISHED状态。服务器收到客户端的应答报文后,服务端也进入ESTABLISHED状态

三次握手第三个报文-ACK报文

从上面的过程可发现第三次握手可携带数据,前两次握手不可携带数据,一旦完成三次握手,双方都处于ESTABLISHED状态,至此连接已建立完成,客户端和服务端就可相互发送数据了。

使用三次握手建立连接的目的:阻止历史重复连接的初始化同步双方的初始序列号避免资源浪费

避免历史连接

防止旧的重复连接初始化造成混乱,网络环境是错综复杂的,往往并不是如期望的一样,先发送的数据包,就先到达目标主机,可能会由于网络拥堵等原因,会使得旧的数据包,先到达目标主机

三次握手避免历史连接

客户端连续发送多次SYN建立连接的报文,在网络拥堵等情况下,一个旧SYN报文最新的SYN报文早到达服务端,此时服务端回一个SYN + ACK报文给客户端,客户端收到后可根据自身的上下文,判断这是一个历史连接序列号过期或超时,客户端就会发送RST报文给服务端,表示中止这一次连接

若是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可在客户端发送方准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:

  • 若是历史连接序列号过期或超时,则第三次握手发送的报文是RST报文,以此中止历史连接
  • 不是历史连接,则第三次发送的报文是ACK报文,通信双方就会成功建立连接
同步双方初始序列号

TCP协议的通信双方,都必须维护一个序列号序列号是可靠传输的一个关键因素,序列号作用:接收方可去除重复数据接收方可根据数据包的序列号按序接收;可标识发送出去的数据包中哪些是已经被对方收到的

序列号在TCP连接中占据着非常重要的作用,当客户端发送携带初始序列号SYN报文时,需要服务端回一个ACK应答报文,表示客户端SYN报文已被服务端成功接收,当服务端发送初始序列号给客户端时,也要得到客户端的应答回应,这样一来一回才能确保双方的初始序列号能被可靠的同步

四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了三次握手,而两次握手只保证了一方的初始序列号能被对方成功接收无法保证双方的初始序列号都能被确认接收

避免资源浪费

若只有两次握手,当客户端SYN请求连接在网络中阻塞,客户端没有接收到ACK报文,就会重新发送SYN,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK确认信号,故每收到一个SYN就只能先主动建立一个连接,若客户端SYN阻塞了,重复发送多次SYN报文,服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。即两次握手会造成消息滞留情况下,服务器重复接收无用的连接请求SYN报文,而造成重复分配资源。

两次握手

TCP连接断开

TCP连接断开-四次挥手

TCP断开连接是通过四次挥手方式,双方都可主动断开连接,断开连接后主机中的资源将被释放,四次挥手过程,每个方向都需要一个FIN和一个ACK,因此通常被称为四次挥手,且主动关闭连接才有TIME_WAIT状态

  • 客户端准备关闭连接时发送一个TCP首部FIN标志位被置为1的报文后,客户端进入FIN_WAIT_1状态
  • 服务端收到FIN报文后向客户端发送ACK应答报文后,服务端进入CLOSED_WAIT状态
  • 客户端收到服务端ACK应答报文后,客户端进入FIN_WAIT_2状态
  • 等待服务端处理完数据后,也向客户端发送FIN报文,之后服务端进入LAST_ACK状态
  • 客户端收到服务端FIN报文后,回一个ACK应答报文后,客户端进入TIME_WAIT状态
  • 服务端收到ACK应答报文后,进入CLOSE状态,至此服务端已经完成连接的关闭
  • 客户端在经过2MSL一段时间后,自动进入CLOSE状态,至此客户端也完成连接的关闭

关闭连接时客户端向服务端发送FIN,仅表示客户端不再发送数据但还能接收数据,服务器收到客户端的FIN报文时,先回一个ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接。服务端通常需要等待完成数据的发送和处理,故服务端ACKFIN一般会分开发送从而比三次握手导致多了一次

TIME_WAIT等待的2MSL时间

MSLMaximum Segment Lifetime报文最大生存时间,它是任何报文在网络上存在的最长时间超过该时间报文将被丢弃。因为TCP报文基于是IP协议的,而IP头中有一个TTL字段,是IP数据报文可经过的最大路由数,每经过一个处理他的路由器TTL值减1,当TTL值为0数据报将被丢弃,同时发送ICMP报文通知源主机

MSL单位是时间,而TTL单位是经过路由条数。故MSL应该要大于等于TTL消耗为0的时间,以确保报文已被自然消亡

TIME_WAIT等待2倍MSL,因为网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,故一来一回需要等待2倍的时间。若被动关闭方没有收到断开连接的最后的ACK报文,就会触发超时重发FIN报文,另一方接收到FIN后,会重发ACK给被动关闭方, 一来一去正好2个MSL。

2MSL时间是从客户端接收到FIN后发送ACK开始计时的,若在TIME-WAIT时间内,因为客户端的ACK没有传输到服务端,客户端又接收到了服务端重发的FIN报文,则2MSL时间将重新计时。在Linux系统里2MSL默认是60秒,则一个MSL30 秒。Linux系统停留在TIME_WAIT的时间为固定的60 秒

TIME_WAIT状态的作用

主动发起关闭连接的一方才会有TIME-WAIT状态,需要TIME-WAIT状态主要是两个原因:防止具有相同四元组的旧数据包被收到保证被动关闭连接的一方能被正确的关闭,即保证最后的ACK能让被动关闭方接收,从而帮助其正常关闭;

防止旧连接的数据包,假设TIME-WAIT没有等待时间或时间过短,被延迟的数据包抵达,服务端在关闭连接之前发送的SEQ = 301报文被网络延迟,此时有相同端口的TCP连接被复用后,被延迟的SEQ = 301抵达客户端,客户端有可能正常接收该过期报文,则会产生数据错乱等严重的问题。故TCP设计出了经过2MSL时间机制,让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的

TIME-WAIT防止旧连接数据包被收到

TIME-WAIT作用是等待足够的时间以确保最后的ACK能让被动关闭方接收,从而帮助其正常关闭。客户端四次挥手的最后一个ACK报文若在网络中被丢失,此时若客户端TIME-WAIT过短或没有,则直接进入了CLOSE状态,服务端会一直处在LASE-ACK状态。当客户端发起建立连接的SYN请求报文后,服务端会发送RST报文给客户端,连接建立的过程就会被终止。

TIME-WAIT等待足够长的情况就会遇到两种情况:服务端正常收到四次挥手的最后一个ACK报文,则服务端正常关闭连接。服务端没有收到四次挥手的最后一个ACK报文时,则会重发FIN关闭连接报文并等待新的ACK报文。故客户端在TIME-WAIT状态等待2MSL时间后,就能保证双方连接都可正常关闭

TIME-WAIT保证连接正确关闭

TCP可靠性传输

为了实现可靠性传输,需要考虑很多事情,如数据破坏丢包重复以及分片顺序混乱等问题。TCP是通过序列号确认应答重发控制连接管理以及窗口控制等机制实现可靠性传输的。

流量控制

发送方不能无脑的发数据给接收方,要考虑接收方处理能力,若一直无脑的发数据给对方,但对方处理不过来,会导致触发重发机制,从而导致网络流量的无端的浪费

为了解决这种现象发生,TCP提供一种机制可让发送方根据接收方的实际接收能力控制发送的数据量,即所谓的流量控制。TCP通过⼀个接收窗口receive window的变量来提供流量控制接收窗口会给发送方⼀个指示到底还有多少可用的缓存空间,发送端会根据接收端的实际接受能力来控制发送的数据量。

接收端主机向发送端主机通知自己可接收数据的大小,发送端会发送不超过该限度的数据,该大小限度就是窗⼝大小,TCP首部有⼀个接收窗口,该字段用于流量控制,用于指示接收方能够够或愿意接收的字节数量

发送端主机会定期发送⼀个窗口探测包 ,该包用于探测接收端主机是否还能够接受数据,当接收端的缓冲区⼀旦面临数据溢出的风险时,窗口大小值也随之被设置为⼀个更小的值通知发送端,从而控制数据发送量。

TCP流量控制

当主机B收到报文段2000-2999之后缓冲区已满,不得不暂时停止接收数据,然后主机A发送窗口探测包,窗口探测包非常小仅仅⼀个字节。然后主机B更新缓冲区接收窗口大小并发送窗口更新通知给主机A,然后主机A再继续发送报⽂段。窗口更新通知可能会丢失,⼀旦丢失发送端就不会发送数据,故窗口探测包会随机发送,以避免这种情况发⽣。

拥塞控制

流量控制是避免发送方的数据填满接收方的缓存,但是并不知道网络的中发生了什么,一般来说计算机网络都处在一个共享的环境。因此可能会因为其他主机之间的通信使得网络拥堵

网络出现拥堵时,若继续发送大量数据包,可能会导致数据包时延丢失等,此时TCP就会重传数据但重传会导致网络负担更重,于是会导致更大的延迟以及更多的丢包,该情况会进入恶性循环被不断地放大。

TCP被设计成一个无私的协议,当网络发送拥塞时,TCP会自我牺牲降低发送的数据量,于是就有了拥塞控制,控制目的是避免发送方的数据填满整个网络,为了在发送方调节所要发送数据的量,定义了一个叫做拥塞窗口

拥塞窗口cwnd发送方维护的一个的状态变量,它会根据网络拥塞程度动态变化,发送窗口swnd和接收窗口 rwnd是约等于的关系,由于入了拥塞窗口,此时发送窗口的值是swnd = min(cwnd, rwnd),即拥塞窗口和接收窗口中的最小值

拥塞窗口cwnd变化的规则:只要网络中没有出现拥塞,cwnd就会增大;若网络中出现拥塞,cwnd就减少;只要发送方没有在规定时间内接收到ACK应答报文,即发生了超时重传就认为网络出现了用拥塞。TCP拥塞控制算法主要包含慢启动拥塞避免快速恢复三部分。

慢启动

当⼀条TCP开始建立连接时,拥塞窗口的值就会初始化为⼀个MSS的较小值,这使得初始发送速率大概是MSS/RTT字节/秒 ,若要传输1000字节的数据RTT为200ms ,则得到的初始发送速率大概是40kb/s。实际情况下可用带宽要比该MSS/RTT大得多,因此TCP想要找到最佳的发送速率可通过慢启动的方式,在慢启动方式中拥塞窗口的值会初始化为1个MSS,且每次传输报问确认后会增加⼀个MSS,拥塞窗口的值会变为2个MSS,这两个报文段都传输成功后每个报文段+1,会变为4个MSS,依此类推每成功⼀次拥塞窗口的值就会翻倍。

慢启动

发送速率不可能会⼀直增⻓,增长总有结束的时候,慢启动通常会使用下面这几种方式结束发送速率的增⻓:

  • 若在慢启动的发送过程出现丢包情况,TCP会将发送方的拥塞窗口设置为1重新开始慢启动过程慢启动阈值初始值就是产生丢包拥塞窗口的值除2,即当检测到拥塞时,慢启动阈值就变成窗⼝值的⼀半
  • 因为当检测到拥塞时,慢启动阈值就是窗口值的⼀半,则当拥塞窗口大于慢启动阈值时,每次翻番都可能会出现丢包,故最好的方式使拥塞窗口值等于慢启动阈值,这样TCP就会转为拥塞控制模式,结束慢启动
  • 检测到3个冗余ACK,TCP就会执行⼀种快速重传并进入恢复状态
拥塞避免

当TCP进入拥塞控制状态后拥塞窗口值就等于拥塞时值的⼀半慢启动阈值。故无法每次报文段到达后都将拥塞窗口值再翻倍。而是采⽤了⼀种相对保守的方式,每次传输完成后只将拥塞窗口的值增加⼀个MSS,如收到10个报问段的确认,但拥塞窗口值只增加⼀个MSS。这是⼀种线性增长模式,它也会有增长阈值,它的增长阈值和慢启动⼀样,若出现丢包,则拥塞窗口值就是⼀个MSS,慢启动阈值就等于拥塞窗口的⼀半;或是收到3个冗余ACK响应也能停止MSS增⻓。若TCP将拥塞窗口值减半后,仍会收到3个冗余ACK,则将慢启动阈值记录为拥塞窗口值的⼀半,进入快速恢复状态。

快速恢复

在快速恢复中,对于使TCP进入快速恢复状态缺失的报⽂段,对于每个收到的冗余ACK拥塞窗口值都会增加⼀个MSS。当对丢失报文段的⼀个ACK到达时,TCP在降低拥塞窗口后进入拥塞避免状态。若在拥塞控制状态后出现超时,则会迁移到慢启动状态拥塞窗口的值被设置为1个MSS,慢启动阈值设置为拥塞窗口的⼀半。

重传机制

TCP实现可靠传输的方式之一,是通过序列号确认应答,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。但在错综复杂的网络,并不一定能顺利正常的数据传输,TCP针对数据包丢失情况用重传机制解决:超时重传快速重传SACKD-SACK

超时重传

发送数据时设定一个定时器,当超过指定的时间后,没有收到对方的ACK确认应答报文,就会重发该数据,TCP 会在数据包丢失确认应答丢失两种情况发生超时重传。

超时重发数据再次超时时,又需要重传时,TCP的策略是超时间隔加倍,即每当遇到一次超时重传时,都会将下一次超时时间间隔设为先前值的两倍。两次超时说明网络环境差,不宜频繁反复发送。超时触发重传超时周期可能相对较长,可使用快速重传机制来解决超时重发的时间等待

快速重传

快速重传不以时间为驱动,而是以数据驱动重传,发送方发出了1,2,3,4,5份数据,第一份Seq1先送到了,于是就Ack回2;结果Seq2因为某些原因没收到,Seq3到达了,于是还是Ack回2;后面的Seq4和Seq5都到了,但还是Ack回2,因为Seq2还是没有收到;

发送端收到了三个Ack = 2的确认,知道了Seq2还没有收到,就会在定时器过期之前,重传丢失的Seq2,最后接收到收到了Seq2,此时因为Seq3,Seq4,Seq5都收到了,于是Ack回6。

快速重传的工作方式是当收到三个相同的ACK报文时,会在定时器过期之前重传丢失的报文段,快速重传机制只解决了一个问题,即超时时间的问题,但它不能解决在重传时是重传之前的一个,还是重传所有的

上面的例子是重传Seq2还是重传Seq2、Seq3、Seq4、Seq5,发送端并不清楚这连续的三个Ack2是谁传回来的,根据TCP不同的实现,以上两种情况都是有可能的。为了解决不知道该重传哪些TCP报文,于是就有SACK方法。

SACK

SACK即Selective Acknowledgment选择性确认,该方式需要在TCP头部选项字段里加一个SACK,它可将缓存的地图发送给发送方,这样发送方就能知道哪些数据收到了,哪些数据没收到,就可只重传丢失的数据

发送方收到了三次同样的ACK确认报文,于是就会触发快速重发机制,通过SACK信息发现只有200~299这段数据丢失,则重发时只选择该TCP段进行重复。若要支持SACK,必须双方都要支持,在Linux下可通过 net.ipv4.tcp_sack参数打开该功能,Linux 2.4后默认打开。

D-SACK

D-SACK即Duplicate SACK,其主要使用了SACK来告诉发送方有哪些数据被重复接收了接收方发给发送方的两个ACK确认应答都丢失了,发送方超时后,重传第一个数据包3000 ~ 3499,于是接收方发现数据是重复收到的,于是回了一个SACK = 3000~3500,告诉发送方3000~3500的数据早已被接收了,因为ACK都是4000,意味着4000之前的所有数据都已收到,故该SACK就代表D-SACK,发送方则知道数据没有丢,是接收方ACK确认报文丢失。

D-SACK ACK丢包

数据包1000~1499被网络延迟了,导致发送方没有收到Ack 1500的确认报文,而后面报文到达的三个相同的ACK 确认报文,就触发了快速重传机制,但在重传后被延迟的数据包1000~1499又到了接收方;故接收方回了一个 SACK=1000~1500,因为ACK已经到了3000,故该SACK是D-SACK,表示收到了重复的包。这样发送方就知道快速重传触发的原因是因为网络延迟。

D-SACK 网络延迟

D-SACK让发送方知道是发出去的包丢了,还是接收方回应的ACK包丢了;可知道是不是发送方的数据包被网络延迟了;可知道网络中是不是把发送方的数据包给复制了;

UDP协议

UDP头部格式

UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务,全称User Datagram Protocol用户数据报协议,UDP为应用程序提供了⼀种无需建立连接就可发送封装的IP数据包的方法,若应用程序开发人员选择的是 UDP,则该应用程序相当于和IP直接打交道

  • 目标端口和源端口:主要是告诉UDP协议应该把报文发给哪个进程
  • 包长度:该字段保存了UDP首部长度和数据长度之和
  • 校验和:为了提供可靠的UDP首部数据而设计

TCP与UDP的区别

TCP与UDP的区别主要体现在连接服务对象可靠性拥塞流量控制首部开销等几个方面

TCP是面向连接传输层协议传输数据前先要建立连接;TCP是一对一的两点服务,即一条连接只有两个端点;TCP是可靠交付数据的,数据可无差错不丢失不重复按需到达;TCP有拥塞控制流量控制机制,保证数据传输的安全性;TCP首部长度较长,会有一定的开销,首部没有使用选项字段时是20个字节,若使用了选项字段会更长;

UDP是不需要建立连接即可传输数据,UDP支持一对一一对多多对多的交互通信;UDP是尽最大努力交付不保证可靠交付数据;UDP没有拥塞控制流量控制机制,即使网络非常拥堵也不会影响UDP发送速率;UDP首部只有8个字节,且是固定不变的开销较小;

TCP和UDP应用场景

由于TCP是面向连接,能保证数据的可靠性交付,因此经常用于:FTP文件传输HTTP / HTTPS

由于UDP面向无连接,可随时发送数据,UDP本身处理既简单又高效,因此经常用于:包总量较少的通信DNSSNMP等、视频、音频等多媒体通信、广播通信