趣谈网络协议学习笔记——TCP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Q52077987/article/details/81556593

什么叫做面向连接?

在两端建立一定的数据结构来维护双方的交互状态。

TCP包和UDP包都会被封装到IP包中,作为IP包数据的一部分在网络上传输。从IP包层看,没有区别。所谓的TCP是面向连接的是指使用了TCP协议的双方会维护一个传输状态的数据结构。为了保证TCP的可靠传输等特性,可以看到TCP头比UDP头复杂得多。

TCP连接的建立过程(3次握手)

理解三次握手,要明白一个现实,就是网络是不可靠的。终究TCP包是要被封装在IP包中的,IP传输本身就是不可靠的,TCP协议要想可靠,只有一个办法,重传。

A要和B建立连接,发送一个建立连接的请求,如同“你好”,B如果收到了“你好”,也应该回复A“你好”。B的回复是必须的,否则,A不知道B是否可达,或者B是否愿意建立这个连接。当A收到B回复的“你好”的时候,A就可以确认它的包B能收到,并且B的包它也能收到,就可以认为连接已经建立好了,但是此时B并不知道A是否能收到它的回答,所以此时B还不能认为连接建立好,需要A再次告诉B,”我已经收到了你的回复”。这样A-B, B-A, A-B,就构成了3次握手。
这里写图片描述

  1. 一开始两端都处于CLOSED状态,B是服务端需要监听端口,监听后变成LISTEN状态。
  2. A发送一个SYN(你好)后状态变为SYN-SENT
  3. B收到SYN后状态由LISTEN变为SYN-RCVD。给A回一个SYN,ACK(回复你好)。
  4. A收到这个SYN,ACK后状态变为ESTABLISHED,然后给B回ACK
  5. B收到ACK后状态变为ESTABLISHED,连接建立完毕。

除了连接状态的变化,3次握手还完成两件细节的敲定。包起始序号和TTL。

包起始序号的意义是这样的:如果序号都从1开始,那么可能存在断开重连又从1开始发送包,但是之前的连接发的包很慢,转了一圈又到达了,造成序号冲突。为了避免这种现象发生,弄了一个计数器,每4ms加1,按32位计算,大约每4个小时循环一次。包的起始序号就按当时技术器的值开始,这样4个小时内发往同一个主机的包序号不会重复。那么4个小时以后呢?一个包的最大寿命是TTL所规定的时间,超过这个时间,路由器就扔了。TTL是绝对不会有那么长时间的。状态时序图中的seq=x就是在做起始序号这个事儿。

TCP连接断开过程(4次挥手)

断开连接因为涉及到数据的完整性,因此比建立连接要复杂。

一个正常的流程就是A和B说:“我没事儿了”,B回复一个“我知道了”,然后B对A说“我也没事儿了”,A说“我知道了”。A-B B-A, B-A A-B,四次挥手。

这中间有一个问题,为什么B不能再回复A“我知道了”的同时告诉A“我也没事儿了“呢?这样不就可以节省一次挥手么?
一个FIN必须是主动发起的。如果B在回复A的时候同时带上FIN,那么A收到后就走关闭流程了。此时B可能还需要处理些事情,就没机会了。那么B又不能说等我处理完事情再给A回复ACK,因为这样A就会不停的重发FIN。所以就先回复A说收到了。等处理完事情再主动告诉A,我也没事儿了。具体需要处理什么事情呢?之后发送过程会看到,TCP的发送是有buffer的,可能此时B Buffer中的数据还没有发送完毕。所以在B发送FIN之前,A还应该是Available的,不能单方关闭连接。

第二个问题是A收到了B的FIN之后,回复ACK,之后等待的这段时间TIME-WAIT在干啥?

第一,如果A发了ACK直接关闭,那么这个ACK如果丢了,B会重试FIN,但是A已经关闭了连接,此时B就会收到RST,这会引起B上报一个连接错误。虽然不会造成数据丢失,但是毕竟不是正常结束。第二,如果A关闭了之后马上又发起了一个向B的连接,不能保证端口号是不同的,如果是相同的,可能会收到垃圾数据。等待2倍的MSL确保网络中没有遗留数据。
状态时序图:
这里写图片描述

TCP包的传输

第一个概念叫累计应答:

为了保证传输的可靠性,所有发送的包都需要接收方确认收到。但是每一个包都回复效率比较低,于是就隔一段回复一个包的序号,表示这个序号之前的包都收到了。这种策略就叫累计应答。

第二个概念叫流量控制:

发送方和接受方数据处理的速度不同,如果是接收方处理的快,那没有什么问题,但是如果是发送方处理的快,接收方一时半会没有地方放新来的包,就会造成包丢失,然后就需要重传,造成浪费。为了避免这种情况发生,引入窗口的概念。接收端给发送端报一个大小叫做窗口,窗口就是接收端能处理的数据包个数。发送端根据这个窗口的大小来决定发送速度。

发送方的缓存数据结构是这样的:
这里写图片描述

LastByteAcked之前是已经发送并收到确认ACK的数据,这部分可以不管它了。LastByteAcked到LastByteSent之间的部分是已经发送了,等待确认的数据包。LastByteSent到AdvertisedWindow之间是没有发送但是可以发送的数据包。这两部分之和应该等于窗口大小。超过窗口大小的数据不应该被发送。

接收端的缓存结构:
这里写图片描述

NextByteExpected之前是已经发送了确认的数据包。这部分数据之所以还在缓存中,是因为上层应用还没有读取,上层读取之后就可以删除了。所以接收端能接受的数据包就是缓存大小减去未被读取的数据大小,这就是接收端给发送端报的窗口大小。

如果接收端应用一直不读取数据,最后NextByteExpected指针和MaxRcvBuffer重合,窗口大小就变成0,发送端就不能再发送数据了。

第三个概念叫拥塞控制

网络能承载的最大数据发送速度达到后,发送方如果继续发送,必然会造成更多的丢包,使网络环境变的更差,造成恶性循环。

为了避免这种情况发生,TCP协议使用慢启动的策略来测探网络承载。TCP连接建立一开始,一次发送包的个数很少,然后使用指数增长的方式迅速增大。当大道一定程度的时候,按照线性增长,一旦发生丢包,就减慢发送速度。这种策略的一个优化算法叫做TCP BBR拥塞算法。

Socket编程:
TCP Socket 调用过程这里写图片描述

猜你喜欢

转载自blog.csdn.net/Q52077987/article/details/81556593