计算机网络

网络(二):传输层TCP、UDP

2021-09-30  本文已影响0人  意一ineyee

目录
一、传输层
二、UDP
 1、UDP的数据格式
 2、UDP头部
三、TCP
 1、TCP的数据格式
 2、TCP头部
 3、TCP是可靠传输(详述)
 4、TCP是面向连接的(详述)

网络(一):基础知识
网络(二):传输层TCP、UDP
网络(三):应用层HTTP
网络(四):应用层HTTPS

一、传输层


传输层有2个协议:

TCP和UDP的区别:

二、UDP


1、UDP的数据格式

UDP的数据格式 = UDP头部 + UDP数据部分(即应用层传下来的真正数据)。

2、UDP头部

UDP是无连接的,而且是不可靠传输,所以它的头部就不需要维护很多复杂的参数来做这两件事,固定占8个字节。

三、TCP


1、TCP的数据格式

TCP的数据格式 = TCP头部 + TCP数据部分(即应用层传下来的真正数据)。

2、TCP头部

TCP是面向连接的,而且是可靠传输,所以它的头部就需要维护很多复杂的参数来做这两件事,至少占20个字节,最多占60个字节,也就是说有40个字节是可选的。

总结:TCP头部有那么多的数据,其中有几个是需要着重关心的。

  • 通常出现在发送方TCP头部里的(当然它肯定也会出现在接收方TCP头部里,只不过我们不需要太关心它的意义而已):
    (1)序号Seq,代表当前我给你发送的是哪个字节开始的数据,接收方收到数据段后就可以根据这个数值做排序。
  • 通常出现在接收方TCP头部里的(当然它肯定也会出现在发送方TCP头部里,只不过我们不需要太关心它的意义而已):
    (1)确认号Ack,必须与确认标志位ACK = 1连用,代表我希望你下次给我发送哪个字节开始的数据,发送方收到数据段后就可以根据这个数值发送相应的数据段。
    (2)窗口Win,用来告诉对方下次最多能给我传输多少个字节的数据,发送方收到数据段后就可以根据这个数值控制发送的数据量了。
  • 同时出现在发送方、接收方TCP头部里的:
    (1)建立连接标志位SYN,当SYN = 1时,代表一个希望跟你建立连接的请求。
    (2)回复标志位ACK,当ACK = 1时,代表我收到了你的消息、给你个回复。
    (3)断开连接标志位FIN,当FIN = 1时,代表一个希望跟你断开连接的请求。

3、TCP是可靠传输

可靠传输是什么?可靠传输是指传输过程中不丢包、数据有序。

为什么要做可靠传输?就是为了保证传输过程中不丢包、数据有序。

怎么做到可靠传输的?

3.1 最初的做法:一段一段传 + 一段一段回复 + 超时重传

假设A要给B发送300个字节的数据,应用层这300个字节的数据到了传输层后会被切成三段M1、M2、M3。(数据分段和数据重组是在OSI参考模型的传输层中完成的)

A会把“M1”先发送给B,B收到“M1”后会给A发送一个“确认收到M1”的回复,A收到“确认收到M1”后就给B发送“M2”......如此循环,直到“M3”发送完毕。

A会把“M1”先发送给B,但是在传输过程中“M1”不小心丢了、根本就没传输到B,或者“M1”没丢、传输到B了、但是B经过差错检验发现这段数据有误、丢掉这段数据,那B根本就不会给A发送“确认收到M1”的回复,那A就迟迟无法收到B的回复、没法发下一段数据了......其实A这边会有一个定时器,过了超时时间如果还没有收到B的回复,A就会认为“M1”传输出错了、进而执行超时重传“M1”......如此循环,直到“M3”发送完毕。

A会把“M1”先发送给B,B收到“M1”后会给A发送一个“确认收到M1”的回复,但是在传输过程中“确认收到M1”不小心丢了、根本就没传输到A,或者“确认收到M1”没丢、传输到A了、但是A经过差错检验发现这段数据有误、丢掉这段数据,那A都会认为“M1”传输出错了、进而重传“M1”,B再次收到“M1”后会丢掉重复的“M1”并再次给A发送一个“确认收到M1”的回复.....如此循环,直到“M3”发送完毕。

A会把“M1”先发送给B,B收到“M1”后会给A发送一个“确认收到M1(第一次)”的回复,但是在传输过程中这个回复可能选了个较远的路径或者网不好,那A就会迟迟无法收到B的回复了,过了超时时间如果还没有收到,A就会认为“M1”传输出错了、进而超时重传“M1”,B再次收到“M1”后会丢掉重复的“M1”并再次给A发送一个“确认收到M1(第二次)”的回复,A收到“确认收到M1(第二次)”后就给B发送“M2”,此时“M2”发出去了,A才收到“确认收到M1(第一次)”,那A就什么都不做、不会再发“M2”了......如此循环,直到“M3”发送完毕。

同理,A会把“M1”先发送给B,如果“M1”迟到了、过了超时时间才到达B,也会导致一连串反应,但是这一套处理可以避免这种情况出错。

不会,重传了N次还是没有成功,会被判定为当前连接出了严重问题,一般都是断开连接、重新建立连接,比如有些操作系统重传了5次还是没有成功,就会发送reset数据段(RST)了。

综上:

一段一段传 + 一段一段回复 + 超时重传可以保证TCP传输的可靠性。我给你传一段你给我个回复,我再传下一段,如果你不给我回复我就给你超时重传、直到你给我回复,这肯定能保证不丢包、数据有序。

但是:

这种做法的传输效率很低,是个串行操作,下一段数据必须得等上一段数据完完全全发送结束后才能发送。

3.2 优化后的做法:多段一起传 + 多段一起回复 + 被动重传(是否使用SACK选择性确认)

假设A要给B发送1200个字节的数据,应用层这1200个字节的数据到了传输层后会被切成十二段M1 ~ M12,每段100个字节。

因为是多段一起传,如果你一下子传太多段,超过了接收方的缓存区大小,就会导致丢包,所以A与B三次握手建立连接时,B会通过窗口Win告诉A下次最多能给它传输多少个字节的数据,比如是400个字节。

那A会把“M1”(序号Seq = 1,代表我给你发送的是第1个字节开始开始的数据)、“M2”(序号Seq = 101,我给你发送的是第101个字节开始开始的数据)、“M3”(序号Seq = 201,我给你发送的是第201个字节开始开始的数据)、“M4”(序号Seq = 301,我给你发送的是第301个字节开始开始的数据)四个数据段一起发送给B(注意:一起发送不是指“M1”、“M2”、“M3”、“M4”合并成一个大段通过一次传输一起发送过去,它们还是四个段,还是四次传输,一起发送是指一次性发起、并行传输,哪一个数据段会先到达也不一定),B收到“M4”后会给A发送一个“确认收到M4”(回复标志位ACK = 1,代表给你个回复,同时也使确认号Ack有意义;确认号Ack = 401,代表B希望A下次给它发送第401个字节开始的数据。注意:B只要回复“确认收到M4”,就意味着它全部收到了“M1”、“M2 ”、“M3 ”、“M4 ”,如果丢失了其中的某一个,它就不会回复“确认收到M4”,可能是回复别的,下面会说到)。

A收到“确认收到M4”后就开始给B发送“M5”、“M6”、“M7”(序号Seq = 601,代表我给你发送的是第601个字节开始开始的数据)、“M8”,但是在传输过程中“M7”不小心丢了,那B收到“M5”、“M6”、“M8”后过了超时时间如果发现“ M7”还没到,它就不会给A发送“确认收到M8”,而是给A发送“确认收到M6”(回复标志位ACK = 1,代表给你个回复,同时也使确认号Ack有意义;确认号Ack = 601,代表B希望A下次给它发送第601个字节开始的数据,因为丢掉的M7刚好就是第601个字节开始的数据)。

A收到“确认收到M6”后就开始给B发送“M7”、“M8”、“M9”、“M10”,但是A发现B发给它的“确认收到M6”是选择性确认(也是三次握手是B告诉A的),于是A又会拿着“确认收到M6”的TCP头部的选项去做判断,发现之前已经成功发过“M8”了,于是就不传“M8”了,只给B发送“M7”、“M9”、“M10”(当然如果三次握手时B告诉A不使用SACK选择性确认,A就会重复发“M8”),那B收到“M7”、“M9”、“M10”就会按照序号Seq把“M7”放在它该在的位置上,保证有序,然后给A发送一个“确认收到M10”......如此循环,直到“M12”发送完毕。

综上:

多段一起传 + 多段一起回复 + 被动重传(是否使用SACK选择性确认)可以保证TCP传输的可靠性。它就是通过TCP头部的序号Seq来保证数据有序,通过被动重传来保证不丢包(当然超时重传是保留下来的)。

同时:

通过多段一起传(是个并行操作) + 多段一起回复 + SACK选择性确认来优化提高了传输效率

3.3 流量控制

流量控制是什么?接收方动态地告诉发送方发送的数据量不要太大,以便接收方能来得及处理。

为什么要做流量控制?如果接收方的缓存区满了,发送方还在疯狂地发送数据,接收方就只能把放不下的数据段丢掉,这样大量的丢包会极大的浪费网络资源,所以要做流量控制。据此我们看出流量控制也是保证TCP不丢包的一个手段。

怎么做到流量控制的?接收方的传输层有个缓存区,它接收到的数据都会先放到缓存区,然后再传递给应用层。接收方会根据缓存区的状态,动态地调整回复TCP数据段里的Win窗口大小,来告诉发送方下次最多能给它传输多少个字节的数据,以此来控制发送方发送的数据量———发送方发送的数据大小不能超过接收方给出的窗口大小,当接收方给出的窗口大小为0时、说明接收方缓存区已经满了、发送方会停止发送数据、如果缓存有了空间、打算让发送发继续发送数据、则发送确认数据窗口大小大于0即可。

3.4 拥塞控制

我们看上图网络中的链路A、链路B、链路C,链路A的带宽为1000M,所以理论上来讲链路B和链路C每秒传输的数据量之和只要小于等于1000M,那链路A就可以不丢包地把数据从路由器3传输到路由器4,但是如果链路B和链路C每秒传输的数据量之和大于1000M,那链路A就会出现过载、进而丢包了,因为它传不了这么多的数据。

拥塞控制是什么?防止过多的数据注入到网络中。(拥塞控制是一个全局的概念,它针对的是整个网络,涉及到了所有计算机、路由器、交换机等,就像全局统筹整个城市的交通那样;而流量控制是一个点对点的概念,它针对的是两台计算机之间,就像国庆期间从长安街东驶入的车辆到长安街西的限流一样。一言以蔽之,拥塞控制控制的是整个网络,流量控制控制的是对方计算机发出的数据量)

为什么要做拥塞控制?避免链路过载、大量丢包。据此我们看出拥塞控制也是保证TCP不丢包的一个手段。

怎么做到拥塞控制的?慢开始 + 拥塞避免 + 快恢复。

4、TCP是面向连接的

连接是什么?这里的连接肯定不是个物理连接(如网线、光纤等),甚至连个虚拟的连接都算不上(虚拟的连接至少让我们感觉到它是个类似于通道一样的东西),这里的连接其实就是指通信双方的一些初始化数据,如通信双方的Win窗口大小、通信双方的MSS每个数据段最大大小、通信双方是否支持SACK选择性确认、通信双方的随机初始序号、通信双方的随机初始确认号等,我们之所以把这些数据称之为“连接”,就是因为通信双方彼此知道对方的这些数据,这种“彼此知道对方数据”的状态——我知道你、你知道我,就好像是有某种“连接”把通信双方关联起来了一样,所以我们才把这些数据称之为“连接”了。(注意:源端口号、目标端口号、源IP、目标IP这四个数据不属于连接的范畴。我们之所以说UDP是无连接的,就是因为它只有这四个数据,而没有通信双方的一些初始化数据,无脑地给对方扔东西;TCP是面向连接的,就是因为它除了这四个数据,还有通信双方的一些初始化数据,有规定地给对方扔东西)所以建立连接就是指通信双方交换彼此的初始化数据,断开连接就是指通信双方清空彼此初始化数据。

为什么要建立连接?为什么要释放连接?因为保证可靠传输那三套机制必须得有一堆初始化数据才能搞,所以我们必须在真正传输数据前先建立连接交换彼此的数据,否则没办法做可靠传输。反过来,当真正的数据传输完了,就清空一下彼此的数据,一方面可以节省内存空间,二方面客户端的端口号是随机的,下次万一这个客户端又随机到这个的端口号(这就意味着源端口号、目标端口号、源IP、目标IP这四个数据可能跟上次全部一样),那大家要是基于上次的数据做数据传输就会出错了。

怎么建立连接的?三次握手建立连接。

假设客户端要向服务器发一个HTTP请求。

一开始客户端处于CLOSED关闭状态,服务器处于LISTEN监听状态。

当客户端要给服务器发送数据的时候,客户端就会发起第一次握手——即发一个“SYN = 1 + 客户端的初始化数据”的TCP数据段给服务器(SYN = 1,代表一个希望跟你建立连接的请求),发出后客户端立即进入SYN-SENT连接请求已发送状态,此时服务器处于LISTEN监听状态。

当服务器收到第一次握手后,就会发起第二次握手——即回复一个“ACK = 1、SYN = 1 + 服务器的初始化数据”的TCP数据段给客户端(ACK = 1,代表我收到了你的消息、给你个回复;SYN = 1,代表一个希望跟你建立连接的请求),发出后服务器立即进入SYN-RCVD连接请求已接收状态,此时客户端处于SYN-SENT同步已发送状态。

当客户端收到第二次握手后,就会发起第三次握手——即回复一个“ACK = 1”的TCP数据段给服务器(ACK = 1,代表我收到了你的消息、给你个回复,发出后客户端立即进入ESTABLISHED连接已建立状态,此时服务器处于SYN-RCVD连接请求已接收状态。

当服务器收到第三次握手后,立即进入ESTABLISHED连接已建立状态,此时客户端处于ESTABLISHED连接已建立状态,连接建立成功。

客户端就可以给服务端发送数据了。

疑问:为什么非要三次握手?既然建立连接就是指通信双方交换彼此的初始化数据,那两次握手就够了呀,客户端通过第一次握手把自己的初始化数据传递给服务器,服务器通过第二次握手把自己的初始化数据传递给客户端,通信双方就都有了彼此的初始化数据,就能够保证可靠传输了呀,那为什么还要第三次握手呢?通俗地回答就是“让服务器知道客户端已经收到了它的初始化数据”,那我们就来分析一下这个通俗的回答到底站不站得住脚,假设根本不存在第三次握手——也就是说服务器只是把初始化数据发出去了、但是根本不知道客户端到底收没收到它的初始化数据,正常情况下是客户端收到了服务器的初始化数据,那双方就是正常地传输数据;异常情况下是客户端没收到服务器的初始化数据——这不就等价于是客户端没收到服务器针对第一次握手的回复嘛,那过了超时时间客户端肯定会发起超时重传,直到收到服务器的初始化数据,如果重传N次还不行,那就断开连接,伺机再连接,所以说两次握手肯定就能保证客户端和服务器都收到对方的初始化数据,因为有超时重传机制存在啊。所以“服务器知不知道客户端收到了它的初始化数据”根本无所谓,它知道了也没有任何意义,它不知道也不会造成任何影响,所以这个通俗的回答有待商榷。那从专业知识的角度回答一下:假设没有第三次握手,客户端发起了第一次握手(第一次),但是在传输过程中这个数据段可能选了个较远的路径或者网不好,迟迟没到达服务器,服务器就没法给客户端回复了,过了超时时间还是没收到,那客户端就会认为第一次握手(第一次)出问题了,于是重新发起第一次握手(第二次),第二次握手顺利到达服务器,服务器也顺利发起了第二握手,接着双方传输数据完毕,四次挥手断开了连接,可就在连接都断开了,很可能客户端发起的第一次握手(第一次)才到达服务器,那此时服务器会认为是个有效的连接请求,于是给客户端发了一个第二次握手的回复,客户端收到第二次握手的回复后,发现我早就跟你传输完数据了,我不想建立连接了啊,于是就不搭理服务器,这样服务器就会白白分配资源监听客户端了,造成服务器资源的浪费。但是如果加上第三次握手——即服务器会在收到第三次握手后才会分配资源监听客户端,那当饶了很大一圈的第一次握手(第一次)到达服务器,服务器虽然依旧会认为是个有效的连接请求,于是给客户端发了一个第二次握手的回复,客户端收到第二次握手的回复后,发现我早就跟你传输完数据了,我不想建立连接了啊,于是就不搭理服务器,于是服务器就不会收到第三次握手,于是服务器就不会分配资源监听客户端了,从而避免了资源浪费。

怎么断开连接的?四次挥手断开连接。

TCP是全双工通信。

假设客户端发现没什么数据要发给服务器了,想要主动断开连接,那客户端就会发起第一次挥手——即给服务器发送一个“FIN = 1”的数据段(FIN = 1,代表一个希望跟你断开连接的请求),代表客户端告诉服务器我已经没有什么东西要发给你了,服务器收到第一次挥手后就会发起第二次挥手——即给客户端发送一个“ACK = 1”的数据段(ACK = 1,代表我收到了你的消息、给你个回复),代表服务器已经知道客户端没有什么东西要发给我了,但是此时还不能断开连接,因为这一个回合下来只代表客户端给服务器发数据这个方向的通道可以关闭了,服务器可还有可能想给客户端传数据呢,所以我们没看到第二次挥手的数据段里有“FIN = 1”。

等到了某个时机,服务器也发现没什么数据要发给客户端了,于是发起第三次挥手——即给客户端发送一个“FIN = 1”的数据段,代表服务器告诉客户端我已经没有什么东西要发给你了,客户端收到第三次挥手后就会发起第四次挥手——即给服务器发送一个“ACK = 1”的数据段,代表客户端已经知道服务器没有什么东西要发给我了,此时双向通道就可以都关闭掉了,于是断开连接。

上一篇下一篇

猜你喜欢

热点阅读