传输层:
UDP
UDP格式
- 16位源端口号(2B 可有可无 看是否要回发)
- 16位目的端口号
- 16位UDP长度(UDP 用户数据报的整个长度)
- 16位UDP检验和(检测整个UDP数据报是否有错)
UDP校验
- 伪首部只在计算校验和的时候才出现,不会进行传输
- 17 UDP 协议的值
- UDP 长度 : UDP 首部 8B + 数据部分长度(不包括伪首部)
发送端: 加上伪首部之后,二进制反码求和 在取反
接收端: 加上伪首部之后,二进制反码求和 在取反 结果为全 1 则代表无差错
TCP
TCP格式
TCP首部:
源端口和目的端口:各占2个字节,分别写入源端口和目的端口;
序号:占4字节, 在一个TCP连接中传送的字节流中的字节流中的每一个字节都按顺序编号,本字段表示本报文段所发送数据的第一个字节的序号
确认号:占4个字节, 期望收到对方下一个报文段得第一个数据字节的序号。若确认号为N,则证明到序号N-1之前的所有数据都以正确收到。
数据偏移: 即TCP首部偏移的长度,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。占4位,以4B为单位即一个数值是4B
保留:占6位,保留为今后使用,但目前应置为0;
6个控制位:
- URG 紧急指针:不用在发送缓存中排队,配合紧急指针使用
- ACK 确认位: ACK = 1,小写的 ack 才有效
- PSH 推送位: 接受放应该尽快交付上层应用,不用等缓存填满
- RST 复位: 表示TCP 连接出现了问题,要释放连接,再重新建立连接
- SYN 同步位:表示是一个连接报文
- FIN 终止位: 表明发送方已经发送完毕,要求释放连接
窗口:指的是发送报文段的一方接收窗口,即现在允许对方发送的数据量。
检验和:检验首部+数据,检验时要加上12B伪首部,第四个字段为6
紧急指针:URG=1时才有意义,指出本报文段中紧急数据的字节数。
选项:最大报文段长度MSS、窗口扩大、时间戳、选择确认…
TCP 和 UDP 区别:
1. 连接
- TCP 是面向连接的传输层协议,传输数据前先要建立连接。
- UDP 是不需要连接,即刻传输数据。
2. 服务对象
- TCP 是一对一的两点服务,即一条连接只有两个端点。
- UDP 支持一对一、一对多、多对多的交互通信
3. 可靠性
- TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
- UDP 是尽最大努力交付,不保证可靠交付数据。
4. 拥塞控制、流量控制
- TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
- UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
5. 首部开销
- TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是
20个字节,如果使用了「选项」字段则会变长的。 - UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
TCP 和 UDP 应用场景:
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
-
FTP文件传输 -
HTTP/HTTPS
由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:
- 包总量较少的通信,如
DNS、SNMP等 - 视频、音频等多媒体通信
- 广播通信
TCP连接管理
三次握手的理解:
(1)目的:三次握手就是为了建立可靠的通信信道,简单来说就是让客户端和服务端确认彼此的发送和接收能力是正常的。
(2)过程:
①第一次握手:客户端发送请求连接报文段,无应用层数据,报文段中同步位SYN=1(表明该报文是一个连接报文),初始化***seq=x(随机值)
服务端收到之后,就表明客户端 的发送能力是正常的,服务端的接收能力是正常的
②第二次握手:服务端收到连接报文后,为该TCP连接分配缓存和变量,并向客户端发送确认报文段。允许连接,此报文段无应用层数据。报文段中,同步位SYN=1,确认位ACK=1,确认号ack= x+1,服务端也会初始化一个***seq=y
客户端收到之后,就能确认,自己的接收和发送是正常的,服务端的发送和接收是正常的
③第三次握手:客户端为该TCP连接分配缓存和变量,并向服务器返回一个确认报文,可以携带数据。此报文段中,SYN=0,ACK=1,seq=x+1,ack=y+1
服务端收到之后,就能确认客户端的接收能力是正常的,自己的发送能力是正常的
最终结果:
至此,客户端和服务端确认了彼此的发送和接收能力都正常,这样客户端和服务端就完成三次握手建立TCP连接了!
相关问题:
为什么需要三次握手,不能两次握手吗?
接下来以三个方面分析三次握手的原因:
-
三次握手才可以阻止历史重复连接的初始化(主要原因)
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵等情况下:
- 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
- 那么此时服务端就会回一个
SYN + ACK报文给客户端; - 客户端收到后可以根据自身的上下文,判断这是一个历史连接(***过期或超时),那么客户端就会发送
RST报文给服务端,表示中止这一次连接。
如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:
- 如果是历史连接(***过期或超时),则第三次握手发送的报文是
RST报文,以此中止历史连接; - 如果不是历史连接,则第三次发送的报文是
ACK报文,通信双方就会成功建立连接;
所以, TCP 使用三次握手建立连接的最主要原因是防止历史连接初始化了连接。
-
三次握手才可以同步双方的初始***
***在 TCP 连接中占据着非常重要的作用:
-
接收方可以去除重复的数据;
-
接收方可以根据数据包的***按序接收;
-
可以标识发送出去的数据包中, 哪些是已经被对方收到的;
四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。
而两次握手只保证了一方的初始***能被对方成功接收,没办法保证双方的初始***都能被确认接收。
-
-
三次握手才可以避免资源浪费
如果只有「两次握手」,当客户端的
SYN请求连接在网络中阻塞,客户端没有接收到ACK报文,就会重新发送SYN,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK确认信号,所以每收到一个SYN就只能先主动建立一个连接,这会造成什么情况呢?如果客户端的
SYN阻塞了,重复发送多次SYN报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求
SYN报文,而造成重复分配资源。
小结:
TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化****。***能够保证数据包不重复、不丢弃和按序传输。
不使用「两次握手」和「四次握手」的原因:
-
「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方***;
-
「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
什么是SYN洪泛攻击?有什么解决策略?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击,SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。
- 其实最常用的一个手段就是优化主机系统设置。比如降低SYN timeout时间,使得主机尽快释放半连接的占用或者采用SYN
- cookie设置,如果短时间内收到了某个IP的重复SYN请求,我们就认为受到了攻击。我们合理的采用防火墙设置等外部网络也可以进行拦截。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
四次挥手的理解:
过程:
(1)第一次挥手:当客户端已经没有要发送的数据了,决定释放连接,就会发送一个终止连接报文,其中FIN=1,seq=u,u的值为之前客户端发送的最后一个序号+1。此时A进入FIN-WAIT-1状态。
(2)第二次挥手:服务端收到该报文后,发送给A一个确认报文,其中ACK=1,ack=u+1,seq=v,v的值为B之前发送的最后一个序号+1。此时客户端进入了FIN-WAIT-2状态,服务端进入了CLOSE-WAIT状态,但连接并未完全释放,服务端会通知高层的应用层结束客户端到服务端这一方向的连接,此时TCP处于半关闭状态。
(3)第三次挥手:当服务端发送完数据后,准备释放连接时就向客户端发送连接终止报文,其中FIN=1,同时还要重发ACK=1,ack=u+1,seq=w(在半关闭状态服务端可能又发送了一些数据)。此时服务端进入LAST-ACK(最后确认)状态。
(4)第四次挥手:客户端收到连接终止报文后还要再进行一次确认,确认报文中ACK=1,ack=w+1,seq=u+1。发送完之后进入TIME-WAIT状态,等待2MSL之后进入CLOSED状态,服务端收到该确认后也进入CLOSED状态。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
由于 TCP 协议是全双工的,也就是说客户端和服务端都可以发起断开连接。两边各发起一次断开连接的申请,加上各自的两次确认,看起来就像执行了四次挥手。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
还有一个原因,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文
TIME_WAIT 过多有什么危害?
如果服务器有处于 TIME-WAIT 状态的 TCP,则说明是由服务器方主动发起的断开请求。
过多的 TIME-WAIT 状态主要的危害有两种:
- 第一是内存资源占用;
- 第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口;
服务器出现了大量CLOSE_WAIT状态如何解决?
大量 CLOSE_WAIT 表示程序出现了问题,对方的 socket 已经关闭连接,而我方忙于读或写没有及时关闭连接,需要检查代码,特别是释放资源的代码,或者是处理请求的线程配置。
TIME_WAIT 和 CLOSE_WAIT 的区别?
-
TIME_WAIT 是主动关闭链接时形成的,等待2MSL时间,约4分钟。主要是防止最后一个ACK丢失。 由于TIME_WAIT 的时间会非常长,因此server端应尽量减少主动关闭连接
-
CLOSE_WAIT是被动关闭连接是形成的。根据TCP状态机,服务器端收到客户端发送的FIN,则按照TCP实现发送ACK,因此进入CLOSE_WAIT状态。但如果服务器端不执行close(),就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多CLOSE_WAIT状态的连接。此时,可能是系统忙于处理读、写操作,而未将已收到FIN的连接,进行close。此时,recv/read已收到FIN的连接socket,会返回0。
TCP 协议如何保证可靠传输
连接管理
连接管理就是三次握手与四次挥手的过程,在前面详细讲过这个过程,这里不再赘述。保证可靠的连接,是保证可靠性的前提
ARQ协议
自动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议和连续ARQ协议。
(1)停止等待ARQ协议
- 停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;
- 在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认;
优点: 简单
缺点: 信道利用率低,等待时间长
(2)连续ARQ协议
连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
优点: 信道利用率高,容易实现,即使确认丢失,也不必重传。
缺点: 不能向发送方反映出接收方已经正确收到的所有分组的信息。 比如:发送方发送了 5条 消息,中间第三条丢失(3号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。
滑动窗口机制
滑动窗口前边的是已发送并且被确认的分组,滑动窗口后边是还没有轮到的分组。滑动窗口里面也分为两块,一块是已经发送但是未被确认的分组,另一块是窗口内等待发送的分组。随着已发送的分组不断被确认,窗口内等待发送的分组也会不断被发送。整个窗口就会往后移动,让还没轮到的分组进入窗口内。
滑动窗口起到了一个限流的作用,也就是说当前滑动窗口的大小决定了当前 TCP 发送包的速率,而滑动窗口的大小取决于拥塞控制窗口和流量控制窗口的两者间的最小值。
流量控制
流量控制就是根据接收端的接收能力动态调整发送端的的发送速度,以确保接收端来的及接收。流量控制主要是通过滑动窗口机制实现的。
TCP首部中就有一个字段来专门通知窗口的大小。将窗口字段设为0则不能发送数据。
在0窗口通知后可能会出现死锁问题(B向A发送了零窗口的报文段后不久,B的接收缓存又有了一些存储空间。于是B向A发送了rwind=400的报文段,然而这个报文段在传送中丢失 了。A一直等待收到B发送的非零窗口的通知,而B也一直等待A发送的数据。这样就死锁了。)。因此。TCP为每个连接设有一个持续计时器。只 要TCP连接的一方收到对方的零窗口通知,就启动持续计时器,若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了最新的窗口大小信息。
拥塞控制
当网络中对资源的需求超过了资源的可用量,导致网络性能变差,网络吞吐量随输入负荷的增大而下降时就表明出现了拥塞。
拥塞控制其实就是尽量减少注入网络的数据,减轻网络中的路由器和链路的负担。
为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
TCP的拥塞控制采用了四种算法,即 慢开始 、 拥塞避免 、快重传 和 快恢复。
-
慢开始: 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1,每经过一个传播轮次,cwnd加倍。
-
拥塞避免: 拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,一旦达到慢开始的初始门限
ssthresh,就进入了拥塞避免阶段。每经过一个往返时间RTT就把发送放的cwnd加1.- 当cwnd<ssthresh时,使用慢开始算法。
- 当cwnd>ssthresh时,改用拥塞避免算法。
-
快重传:快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。快重传策略是为了防止TCP连接因等待重传计时器超时而空闲较长的时间。
-
快恢复:快重传和快恢复是搭配使用的,快重传完成后,立即执行快恢复算法。将ssthresh门限设置为当前拥塞窗口的一般,之后将拥塞窗口设置为新的ssthresh门限(即减半), 进入拥塞避免阶段。
这里可能会有人有疑问,为什么不直接进入慢开始阶段,更彻底得避免拥塞。主要的原因是考虑到如果网络出现拥塞得话,就不会收到多次重复确认,所以发送方认为网络可能没有出现拥塞,所以不执行慢开始算法,而是将cwnd设置为新得ssthresh门限,执行拥塞避免算法
流量控制和拥塞控制的区别:
流量控制是一个端到端的问题,
而拥塞控制是一个全局性问题,设计到所有的主机、所有的路由器。