TCP/IP知识脑图
2020-05-11 15:02:15 0 举报
AI智能生成
TCP基础概念、高级概念、三次握手、四次挥手、滑动窗口所有知识点一应俱全
作者其他创作
大纲/内容
TCP的状态积
网络上的传输是没有连接的,包括TCP也是一样的。而TCP所谓的“连接”,其实只不过是在通讯的双方维护一个“连接状态”,让它看上去好像有连接一样。所以,TCP的状态变换是非常重要的。
- SYN:同步序列编号(Synchronize Sequence Numbers),其作用是用于发起一个新链接的标识位。在TCP协议中规定,不能带数据,但是占一位。
- ACK:(Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。如果ACK为1表示确认号有效,如果为0表示报文中不包含确认信息。在TCP中规定,能带数据,不带数据时不占位数。
- PSH(push传送):该报文希望,到达对端时,将这个报文及缓存区之间缓存尚未交付的数据一并交付给进程。
- FIN(finish结束) :结束连接
- RST(reset重置) :重新连接
- URG(urgent紧急):只有紧急数据
基础概念
TCP(Transmission Control Protocol)传输控制协议
TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。
Sequence Number是包的序号,用来解决网络包乱序(reordering)问题,表示当前端成功发送的数据位数
Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题,当前端成功接收的数据位数
Window又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。
TCP Flag ,也就是包的类型,主要是用于操控TCP的状态机的。
注意在头部有Sequence Number/Acknowledge number,此外还有两个状态积(Flag)也叫SYN和ACK,它的值只有0和1,要注意区分。
TCP滑动窗口
产生的背景
TCP要解决一个很大的事,那就是要在一个网络根据不同的情况来动态调整自己的发包的速度,小则让自己的连接更稳定,大则让整个网络更稳定,所以为了解决网络传输不可靠的问题,出现了滑动窗口协议。
解决了什么问题
TCP必需要解决的可靠传输以及包乱序(reordering)的问题
所以,TCP必需要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引起网络拥塞,导致丢包。
所以,TCP必需要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引起网络拥塞,导致丢包。
入门
https://juejin.im/post/5c9f1dd651882567b4339bce
深入了解
https://coolshell.cn/articles/11609.html#TCP%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3
高级概念
什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s......
这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s......
Frame&Packet&Segment
- 传输层(TCP/UDP层):传输层就是负责将数据进行可靠或者不可靠传递,负责终端之间的传送。如:TCP、UDP。单位:Segment
- 网络层(IP层):网络层就是负责选择最佳路径,并保证数据始终沿着最佳路径传输。路由器的功能就是选合适的路径。单位:Packet
- 网络接口层:实际上TCP/IP标准并不定义与ISO数据链路层和物理层相对应的功能。相反,它定义像地址解析协议(Address Resolution Protocol,ARP)这样的协议,提供TCP/IP协议的数据结构和实际物理硬件之间的接口。单位:Frame
MTU
MTU(Maximum Transmission Unit)最大传输单元,在TCP/IP协议族中,指的是IP数据报能经过一个物理网络的最大报文长度(是IP层的数据包大小),其中包括了IP首部(从20个字节到60个字节不等),一般以太网的MTU设为1500字节,加上以太帧首部的长度14字节,也就是一个以太帧不会超过1500+14 = 1514字节。
MSS
MSS(Maximum Segment Size,最大报文段大小,指的是TCP报文(一种IP协议的上层协议)的最大数据报长度(TCP层包的大小),其中不包括TCP首部长度。MSS由TCP连接的过程中由双方协商得出,其中SYN字段中的选项部分包括了这个信息。如果MSS+TCP首部+IP首部大于MTU,那么IP报文就会存在分片,如果小于,那么就可以不需要分片正常发送。
MSL
Maximum Segment Lifetime,因为它是Segment,所以它是TCP层面的限制,也就是「报文最大生存时间」。我们知道,TCP整个报文段是在IP数据报内的,而IP数据报的生存时间又依赖于TTL字段
TTL
ip头中有一个TTL域,TTL是 time to live的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个ip数据报可以经过的最大路由数,每经 过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。
TTL与MSL是有关系的但不是简单的相等的关系,MSL要大于等于TTL。原因是TCP包是在IP数据包里面,TCP包理论来讲应该有一个更大的生存时间,否则IP包送到了,里面的包失效了。
TTL与MSL是有关系的但不是简单的相等的关系,MSL要大于等于TTL。原因是TCP包是在IP数据包里面,TCP包理论来讲应该有一个更大的生存时间,否则IP包送到了,里面的包失效了。
2MSL
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态。
对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。因为一个包的最大时间可能是TTL的时间,而这个时间又略大于MSL,所以需要2MSL。
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。因为一个包的最大时间可能是TTL的时间,而这个时间又略大于MSL,所以需要2MSL。
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
三次握手
握手流程
- TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
- TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
- TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
- TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
- 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
握手中的状态
- LISTEN 表示socket已经处于listen状态了,可以建立连接;
- SYN_SENT 表示socket在发出connect连接的时候,会首先发送SYN报文,然后等待另一端发送的确认报文(ACK),表示这端已经发送完SYN报文了;
- SYN_RCVD 表示一端已经接收到SYN报文了;
- ESTABLISHED 表示已经建立连接了,可以发送数据了。
注意点
- SYN_SENT这个状态,只要发送完就是这个状态,不管是否成功
- SYN_RCVD这个只要收到SYN就进入这个状态
- 只要发了SYN并且收到了这次SYN回的ACK即进入ESTABLISHED状态,也就是说Client会单方面先进入ESTABLISHED状态,至于Server要依赖Server自己是否收到ACK来自己控制这个状态。
其实根据这个状态变化,我们知道一件事,其实网络世界不存在什么真正的长链接,我们所说的连接状态也是自己维护的一个状态机,只要我给另外一个设备发消息,能发能收,那我就ready了,我就处于ESTABLISHED状态,所谓的连接只是一个自己维护的一个有状态机。
三次握手中都干了什么事?
- 主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。
- 交换 TCP 窗口大小信息
交换Sequence Number的意义?
在三次握手期间,C/S两端交换自己的Sequence Number初始值,就是第一次Client->Server发Sequence Number后,就奠定了Server的Acknowledge number的值,第二次Server->Client发Sequence Number就奠定了Client的Acknowledge number。因为在握手过程标识位都占用了一位,所以Sequence Number和Acknowledge number都会加1,后续传输过程中再加上Length就是它们的取值了。
Acknowledge number = 对方上次发过来的Sequence number + Length
Sequence number = 对方上次发过来的Acknowledge number
Acknowledge number = 对方上次发过来的Sequence number + Length
Sequence number = 对方上次发过来的Acknowledge number
为什么要三次握手?而不是四次五次更多次
在《计算机网络》一书中其中有提到,三次握手的目的是“为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误”。
这种情况是:一端(client)A发出去的第一个连接请求报文并没有丢失,而是因为某些未知的原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间才到达另一端(server)B。本来这是一个早已失效的报文段,但是B收到此失效的报文之后,会误认为是A再次发出的一个新的连接请求,于是B端就向A又发出确认报文,表示同意建立连接。如果不采用“三次握手”,那么只要B端发出确认报文就会认为新的连接已经建立了,但是A端并没有发出建立连接的请求,因此不会去向B端发送数据,B端没有收到数据就会一直等待,这样B端就会白白浪费掉很多资源。如果采用“三次握手”的话就不会出现这种情况,B端收到一个过时失效的报文段之后,向A端发出确认,此时A并没有要求建立连接,所以就不会向B端发送确认,这个时候B端也能够知道连接没有建立。
问题的本质是,信道是不可靠的,但是我们要建立可靠的连接发送可靠的数据,也就是数据传输是需要可靠的。在这个时候三次握手是一个理论上的最小值,并不是说是tcp协议要求的,而是为了满足在不可靠的信道上传输可靠的数据所要求的。
其实四次,四百次都不可能保证是真的可靠,只要双方的消息都有去有回,就基本可以了,三次只是一个最小理论值。
这种情况是:一端(client)A发出去的第一个连接请求报文并没有丢失,而是因为某些未知的原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间才到达另一端(server)B。本来这是一个早已失效的报文段,但是B收到此失效的报文之后,会误认为是A再次发出的一个新的连接请求,于是B端就向A又发出确认报文,表示同意建立连接。如果不采用“三次握手”,那么只要B端发出确认报文就会认为新的连接已经建立了,但是A端并没有发出建立连接的请求,因此不会去向B端发送数据,B端没有收到数据就会一直等待,这样B端就会白白浪费掉很多资源。如果采用“三次握手”的话就不会出现这种情况,B端收到一个过时失效的报文段之后,向A端发出确认,此时A并没有要求建立连接,所以就不会向B端发送确认,这个时候B端也能够知道连接没有建立。
问题的本质是,信道是不可靠的,但是我们要建立可靠的连接发送可靠的数据,也就是数据传输是需要可靠的。在这个时候三次握手是一个理论上的最小值,并不是说是tcp协议要求的,而是为了满足在不可靠的信道上传输可靠的数据所要求的。
其实四次,四百次都不可能保证是真的可靠,只要双方的消息都有去有回,就基本可以了,三次只是一个最小理论值。
握手期间丢包怎么办(超时重传机制)?
(1) 如果第一个包,A发送给B请求建立连接的报文(SYN)如果丢掉了,A会周期性的超时重传,直到B发出确认(SYN+ACK);
(2) 如果第二个包,B发送给A的确认报文(SYN+ACK)如果丢掉了,B会周期性的超时重传,直到A发出确认(ACK);
(3) 如果第三个包,A发送给B的确认报文(ACK)如果丢掉了,
(2) 如果第二个包,B发送给A的确认报文(SYN+ACK)如果丢掉了,B会周期性的超时重传,直到A发出确认(ACK);
(3) 如果第三个包,A发送给B的确认报文(ACK)如果丢掉了,
- A在发送完确认报文之后,单方面会进入ESTABLISHED的状态,B还是SYN_RCVD状态
- 如果此时双方都没有数据需要发送,B会周期性的超时发送(SYN+ACK),直到收到A的确认报文(ACK),此时B也进入ESTABLISHED状态,双方可以发送数据;
- 如果A有数据发送,A发送的是(ACK+DATA),B会在收到这个数据包的时候自动切换到ESTABLISHED状态,并接受数据(DATA);
- 如果这个时候B要发送数据,B是发送不了数据的,会周期性的超时重传(SYN+ACK)直到收到A的确认(ACK)B才能发送数据。
三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
SYN攻击是什么?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
为什么Sequence number和Acknowledge number的初始值(ISN initialization sequence number)是一个随机值?
ISN是不能hard code的,不然会出问题的——比如:如果连接建好后始终用1来做ISN,如果client发了30个segment过去,但是网络断了,于是 client重连,又用了1做ISN,但是之前连接的那些包到了,于是就被当成了新连接的包,此时,client的Sequence Number 可能是3,而Server端认为client端的这个号是30了。全乱了。RFC793中说,ISN会和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。因为,我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime(缩写为MSL – Wikipedia语条),所以,只要MSL的值小于4.55小时,那么,我们就不会重用到ISN。
四次挥手流程
挥手流程
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
挥手中的状态
- FIN_WAIT_1 表示在等待另一方的FIN报文,和FIN_WAIT_2的区别是,FIN_WAIT_1表示socket现在要主动关闭连接,在发送完FIN报文后socket进入FIN_WAIT_1状态,当收到另一方发送FIN的ACK之后立即进入FIN_WAIT_2状态;
- FIN_WAIT_2 同上,此时需要做的事情是可能还会接收数据,然后等待另一方的FIN;也就是说此时发送的通路已断,接收的通路还是正常的。
- TIME_WAIT 存在主动关闭的一方,表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL(Max Segment Lifetime))后即可回到CLOSED可用状态了,需要等一段时间时原因是网络是不可靠的,不能保证这个ACK发送成功了,如果失败了,对端会超时重传FIN;
- CLOSING 表示在发送FIN之后,没有收到对方的ACK,而是收到了对方的FIN,这中情况很少见,只有在两端几乎同时关闭同一个socket的时候才会出现CLOSING状态;
- CLOSE_WAIT 表示收到对方的FIN之后,回给对方ACK,此时处于CLOSE_WAIT状态,等待关闭,要看自己是否还有数据要发送;
- LAST_ACK 表示收到对方的FIN之后,回给对方ACK,然后自己也要关闭发送FIN,等待另一方的ACK时候的状态;
- CLOSED 这个状态表示连接已经断开。
挥手为什么需要四次?
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
四次挥手释放连接时,等待2MSL的意义
1、为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。
2、他还可以防止已失效的报文段。客户端在发送最后一个ACK之后,再经过经过2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
注意:在服务器发送了FIN-ACK之后,会立即启动超时重传计时器。客户端在发送最后一个ACK之后会立即启动时间等待计时器。
2、他还可以防止已失效的报文段。客户端在发送最后一个ACK之后,再经过经过2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
注意:在服务器发送了FIN-ACK之后,会立即启动超时重传计时器。客户端在发送最后一个ACK之后会立即启动时间等待计时器。
0 条评论
下一页