当前位置: 首页 > 后端技术 > Node.js

网络协议8-TCP(上)

时间:2023-04-03 18:19:16 Node.js

网络协议1-网络协议概述2-IP是怎么来的,为什么消失了?网络协议3-从物理层到MAC层网络协议4-交换机和VLAN:办公室太复杂,我要回学校了网络协议5-ICMP和ping:问路的童子军网络协议6-路由协议:敢问路在何方?网络协议7——UDP协议:星山在世界遇到城市就可以玩了。与之对应的是,TCP就像一个大哥,明白了社会的残酷,变得复杂成熟,坚持“性恶论”。它认为网络环境恶劣,丢包、乱序、重传、拥塞是家常便饭。如有异议,可能会造成丢包,无法派送。因此,从算法层面保证了可靠性。TCP头格式?老规矩,我们先来看看TCP头的格式。从上图可以看出,它比UDP要复杂的多。而复杂的地方正是解决UDP问题所需要的领域。首先,源端口号和目的端口号都是不可或缺的字段。接下来是包的序列号。包编号的目的是为了解决乱序问题。大哥做事以谨慎为主,一个接一个,不管情况多么复杂,临危不乱,他都会保持冷静。除了发送方需要给数据包编号外,接收方也会回复一个确认序号。做事靠谱,答应做的就一定做,暂时做不到的一定要给答复。这里要注意,TCP是个老大哥,但不能说他一定会保证准确无误的完成传输。从IP层面来说,如果网络真的那么差,可靠性是没有保障的。TCP大哥再稳,也控制不了IP层的丢包。他只能尽可能保证他这个级别的可靠性。性别。然后是一些状态位。常见的状态位有以下几种:SYN(SynchronizeSequenceNumbers,同步序列号):发起连接ACK(Acknowledgment,确认符):回复RST(Connectionreset):重新连接FIN:结束连接可见,TCP是基于“性恶论”的,所以非常警惕。不像UDP和孩子,任何一个不知道的孩子都可以一起玩,他和别人之间的信任只能通过多次互动才能建立起来。还有一个窗口大小。这被TCP用于流量控制。通信双方声明一个窗口来标识自己当前的处理能力,这样发送端就不会发送得太快,否则接收端就会不堪重负。不能发得太慢,不然接收端会饿死的。根据上面对TCP头的分析,我们知道对于TCP协议要重点关注以下几个问题:顺序问题,稳定不受干扰;丢包问题,承诺靠谱;联系维豪,自始至终;,知进知退。TCP的三次握手了解了TCP头之后,我们再来看一下TCP建立连接的过程,也就是著名的“三次握手”。三次握手的过程如下:A:你好,我是A(SYN)。B:你好A,这里是B(SYN,ACK)。A:你好B(ACK的ACK)。重点记住上面的过程,后面的很多分析都是基于这个过程。记得刚开始接触三次握手的时候,一直在想为什么一定要三次呢?不能做两次吗?不能四次吗?然后很多人就解释了,两次了又怎样,四次又怎样?但这实际上是以果推因,没有说明本质。要知道握手是为了建立稳定的连接,这是最终的目的。要实现这一目标,需要双方的互动形成一个确认的闭环。以上面A和B的通信为例,A给B发消息,B想告诉A他收到了消息。这个时候是不是确认闭环了?显然不是,因为B还没有收到A的确认消息。所以要达到我们上面的目的,A必须给B一个确认消息,这样就形成了一个确认闭环。A向B发送确认报文后,如果网络不好,也会出现丢包的情况。按理说应该会有回应,但是我们发现,好像再这样下去,就没有比赛了。所以,我们说只要通信双方形成一个确认闭环,就认为连接已经建立。一旦连接建立,A马上发送数据,A发送数据,后面的很多问题都解决了。比如A发给B的确认信息丢失了。当A后续发送的数据到达时,B就可以认为连接已经建立。如果B直接挂了,A发送的数据会报错,说B不可达,这样A也知道B出事了。三向握手不仅是建立通信双方的连接,也用来传递TCP数据包的序号。A需要告诉B我发起的包的序号是从哪个序号开始的,B也需要告诉AB发起的包的序号是从哪个序号开始的。TCP报文的序号会随着时间变化,可以看做一个32位的计数器,每4ms加1。计算一下,要4个多小时才会出现重号。然而4个小时后,还没有到达目的地的包已经死了。这是因为IP标头中的TTL(生存时间)。为什么序列号不能从1开始?因为这样很容易引起冲突。比如A连接B后,发送了1、2、3三个包,但是发送3的时候,中途丢了或者绕了路,所以重发了。后来A掉线了,重新连上B后,序号又从1开始,然后发送2,但是根本不想发送3,如果上次绕路的3刚好回来发送它给B,B自然会认为这是下一个数据包,于是就发生了错误。就这样,双方历经千辛万苦,终于建立了联系。前面说过,为了保持连接,双方都需要维护一个状态机。在连接建立过程中,双方的状态变化时序图如下:总体过程为:客户端和服务端都处于CLOSED状态;服务器主动监听某个端口,处于LISTEN状态;客户端发起SYN连接,处于SYN-SENT状态。服务器收到客户端发起的连接,返回SYN,并ACK客户端的SYN,处于SYN-RCVD状态;客户端收到服务器发送的SYN和ACK后,发送ACK的ACK,处于ESTABLISHED状态;服务器收到ACK到ACK后,处于ESTABLISHED状态。四波TCP结束连接后,我们来了解一下TCP的“再见模式”。这也常被称为四浪。同样以A和B为例,挥手的过程:A:B,我不想跟你玩了。B:哦,我明白了,你不想玩了。这时候只是A不想玩了,也就是说A不会再发送数据了,但是此时B还没有完成自己的工作,还能发送数据,所以B在此时处于半封闭状态。B:A,好吧,我不想再跟你玩了,再见。A:好的,再见。此连接已关闭。看起来过程很顺利,没错,这就是书信中双方“和平分手”的一幕。A开始说“我不想玩了”,B说“知道了”,这一局,没有问题,因为在此之前,双方还处于合作状态。如果A说“Nomoreplaying”没有收到回复,那么A会重新发送“Nomoreplaying”。但是这一轮结束之后,很可能会出现不正常的情况,因为先撕破脸的是一方。这种撕脸有两种情况。一种情况是A说“别玩了”后,A直接跑了,这是有问题的,因为B还没有发起结束,如果A直接跑了,即使B发起结束,也得不到Answer,B不知道该怎么办。另一种情况是,A说“别玩了”后,B直接跑了。这也是有问题的,因为A不知道B是不是还有事情要处理,也不知道过段时间发送会不会结束。为了解决这些问题,TCP专门设计了几种状态来处理这些问题。接下来我们看一下断开连接时的状态时序图。总体流程是:A说“不用了”,进入FIN_WAIT_1状态;B收到消息“A不玩”,回复“我知道”,进入CLOSE_WAIT状态;A收到“B说我知道”,进入FIN_WAIT_2状态。这时候如果B直接跑掉,A就会一直处于这个状态。TCP协议不处理这种状态,但Linux有。可以调整参数tcp_fin_timeout,设置超时时间;B没有跑掉,发送了“B不玩了”的消息,处于LAST_ACK状态;A收到“B说不玩了”的消息和“A知道B不玩了”的消息后,从FINE_WAIT_2状态结束。最后一步,如果A直接跑了,也会出问题。因为A最后的回复,如果B没有收到,会重复第4步,但是因为A已经跑掉了,所以B会一直重复第4步。结尾。此等待时间为TIME_WAIT。这个时间一定要足够长,这样如果B没有收到A的回复,B会重新发送给A,A的回复必须有足够的时间到达B。A跑路的另一个问题是A的端口是空闲的,但是B并不知道原来B发送的很多包可能还在路上。如果A的端口被新的应用占用了,新的应用就会收到B在上一次连接中发来的包。虽然序列号是重新生成的,但是这里会有一个双保险,防止混淆。因此,A也需要等待足够长的时间,直到B发送的所有未到达的包都“死掉”,然后腾出端口。等待时间设置为2MSL,MSL是MaximumSegmentLifetime,即数据包的最大生存时间。它是任何数据包在网络上存在的最长时间,超过这个时间的数据包将被丢弃。因为TCP报文是基于IP协议的,IP报头中有一个TTL字段,它是IP数据报可以通过的最大路径数。每经过一个对其进行处理的路由器,这个值就会减1。当值为0时,数据报就会被丢弃,同时发送一个ICMP包通知源主机。协议规定MSL为2分钟,实际应用中常用的取值有30秒、1分钟、2分钟。还有一种异常情况,B超过了2MS的时间,仍然没有收到它发送的FIN的ACK。按照TCP的原理,B当然会重发FIN。这时,A再次收到这个包后,就说明我等了你这么久。RST,让B知道A跑了。TCP状态机结合了连接建立和连接断开两个时序状态图,这就是大名鼎鼎的TCP状态机。我们可以将这个状态机与顺序状态机进行比较,会更加清晰。图中黑色粗体部分就是上面提到的主要流程。相关说明:阿拉伯数字序号:连接建立顺序;大写中文数字序号:断开顺序;粗实线:客户端A的状态变化;添加粗虚线:服务器B的状态转换;总结一下,TCP头非常复杂,我们主要关注五个问题。顺序问题、丢包问题、连接维护、流量控制、拥塞控制;建立连接时握手三次,断开时挥手四次,状态图一定要牢记。参考:百度百科-TCP入门;刘超-浅谈网络协议系列课程;