💡TCP的可靠不在于它是否可以把数据100%传输过去,而是
- 1.发送方发去数据后,可以知道接收方是否收到数据;
- 2.如果接收方没收到,可以有补救手段;
TCP的可靠性是付出代价的,即传输效率和复杂程度 。
1.确认应答
发送方把数据发给接收方,接收方收到数据需要给发送方返回一个应答报文(确认序列号)(ackownledge,ack),发送方收到应答报文,说明数据发送成功。
发送方连续发多条数据的情况,这种情况会存在"后发先至"的情况。所以引入"序列化",此处TCP需要完成:
1.确保发出去的数据与应答报文可以对号;
2.确保在出现后发先至情况下可以按正确顺序解答;
*序号按照字节编号,初始序号一般不从1开始。通过使用序列号(Sequence Number,SEQ)和确认序列号(ACK),TCP协议可以实现可靠的数据传输。序列号用于标识和排序报文段,而确认序列号用于确认已经接收到的报文段。
eg:当第一个数据报中有1000个字节的载荷数据,其中第一个字节的序号是1,就在TCP报头序号中写1,即载荷数据中只记录这次传输的载荷数据的第一个字节的序号,此时ack写入1001并返回。此时发送方就知道刚才发送的1~1000到了。——>可靠传输(确认应答就起到最核心作用)
在图一中,标志位ack为1表示它是应答报文,此时数据报中的"确认序号"字段就能生效。
2.超时重传
¹引入
上述确认应答是理想的状态,网络传输中可能会出现丢包或者网络延迟,TCP的超时重传可以保证可靠数据传输。当数据段丢失时,发送方通过超时重传机制,能够及时重新发送数据,确保接收方最终能够正确接收到数据。
💡为啥会出现丢包呢?
网络拥塞:数据流量超过网络链路的容量,过多的数据包同时竞争有限的带宽资源,导致一些数据包无法及时传输或被丢弃。缓冲区溢出:当接收方的缓冲区已满,无法及时处理接收到的数据包时,会导致数据包被丢弃。
了解:
Linux中超时以500ms为一个单位进行控制,每次判定超时重发的时间都是500ms的整数倍。重发一次还无应答就会以2*500在进行重传。仍得不到应答就等待4*500ms,...... ,指数递增。累计到一定程度TCP会认为网络对端主机有异常,强制关闭连接。
²丢包类型
在第一点确认应答情况下可能会出现两种类型的丢包:
1.传输的数据丢了
2.返回的ack丢了
这两种情况发送方是无法区分的,所以引入定时器。只不过这个定时器等待的时间会动态变化。如果时间等待的足够长,那就会触发TCP的重置连接操作。
³过程
当发送方向接收方发送数据时,会启动一个定时器,等待接收方对已发送数据的确认。如果在设定的超时时间内没有收到确认,发送方会认为数据丢失,并进行超时重传。
具体的超时重传过程如下:
- 1. 发送方发送数据段,并启动定时器:发送方将数据段发送给接收方,同时启动一个定时器,等待确认。
- 2. 接收方收到数据段并发送确认:接收方收到数据段后,会发送一个确认(ACK)给发送方,确认接收到了数据段。
- 3. 发送方收到确认:发送方收到接收方的确认后,停止定时器,并继续发送下一个数据段(如果有)。
- 4. 定时器超时:如果在设定的超时时间内,发送方没有收到接收方的确认,发送方认为数据丢失,gaolian:发送方重新发送未收到确认的数据段,并重新启动定时器,等待新的确认。
⁴注意
TCP的超时重传机制可能会导致网络的拥塞,因为它会在丢包的情况下进行重传,增加了网络的负载。因此,TCP采用了一系列的拥塞控制算法来避免过多的重传造成网络拥塞。
如果因为超时重传,其实上接收方接收到两个数据,inputStream.read读取到两个一样的数据,那❌,会有严重的后果。eg:我去付款,结果因为重传,导致扣我两份的钱!!!
TCP的解决办法是:接收缓冲区,保存当前收到的数据以及序号,如果已经存在于接收缓冲区,接收方会进行去重。而且TCP还会进行重新排序,确保程序发送的顺序和应用程序读取的顺序一致。
(太厉害了,TCP设计人考虑好全面)
3.连接管理
是对TCP可靠性的补充。TCP的连接管理包括三次握手和四次挥手,用于建立和关闭连接。建立连接是客户端发起,断开连接是客户端和服务器都可以主动发起。
三次握手(Three-Way Handshake)
- 第一次握手(SYN):客户端发送一个带有SYN标志的TCP报文段给服务器端,请求建立连接。此时客户端进入SYN_SENT状态。syn同步数据段,特殊的TCP数据包,没有载荷的(不携带业务数据,业务数据就是应用层数据包)当TCP组成中的六位标志位的syn为1表示它为同步报文段
- 第二次握手(SYN+ACK):服务器端收到客户端的SYN报文段后,发送一个带有SYN和ACK标志的TCP报文段作为响应,确认客户端请求,并提出自己请求建立连接。此时服务器端进入SYN_RECEIVED状态。都是内核触发的,同一时机可以合并。
- 第三次握手(ACK):客户端收到服务器端的SYN+ACK报文段后,向服务器端发送一个带有ACK标志的TCP报文段,确认连接建立。此时客户端和服务器端都进入已建立连接的ESTABLISHED状态。
- listen状态:服务其创建好socket,等待客户端连接;established建立连接完成;
三次握手:
1.确定网络(通信路径)畅通;
2.确定接收方和发送方的发送能力接受能力正常;
3.双方对重要信息进行协商:比如序号,确认号,窗口大小
协商的数据也避免了前朝的剑斩本朝的官
syn确认了对方身份,ack保证了序列化
四次挥手(Four-Way Handshake):
- 第一次挥手(FIN):当客户端想要关闭连接时,内核发送一个带有FIN标志的TCP报文段给服务器端,表示不再发送数据。此时客户端进入FIN_WAIT_1状态。哪方主动断开哪方timewait,timewait存在的意义就是避免最后一个ack丢失,如果没有timewait状态,导致客户端直接closed,服务器发起两次 fin,甚至更多,就永远收不到ack了。so是为了处理重传的FIN,这边返回ack,fin才有意义。等待时间2msl
- 第二次挥手(ACK):服务器端收到客户端的FIN报文段后,发送一个带有ACK标志的TCP报文段作为确认。此时服务器端进入CLOSE_WAIT状态,而客户端进入FIN_WAIT_2状态。
- 第三次挥手(FIN):当服务器端也准备关闭连接时,应用程序发送一个带有FIN标志的TCP报文段给客户端(server在socket对象调用close方法发起fin/进程结束),表示不再发送数据。此时服务器端进入LAST_ACK状态。两次触发fin时机不同。具体相差时机看代码。如果close没写或者没执行到,第二个fin有可能发不出去。如果正常四次挥手,那就正常断开连接;否则异常流程断开连接。
- 第四次挥手(ACK):客户端收到服务器端的FIN报文段后,发送一个带有ACK标志的TCP报文段作为确认。此时客户端进入TIME_WAIT状态,服务器端收到确认后进入CLOSED状态。
通过三次握手,客户端和服务器端建立起可靠的连接。而通过四次挥手,双方完成数据传输后,优雅地关闭连接。这样可 以确保数据的可靠性和完整性,并释放连接使用的资源。
但是tcp的延时应答可以延时ack的时间,ack和fin就可能可以合并。
4.滑动窗口(效率)
为了尽可能提高TCP传输的效率, 引入了滑动窗口机制。TCP滑动窗口(TCP sliding window)是用于流量控制和拥塞控制的一种机制。它表示发送方在不等待确认的情况下可以发送的数据量。在一定范围内窗口越大,传输效率越高。如果通信双方传输数据量大,比较频繁就会用到滑动窗口——快速重传。
滑动窗口是通过TCP协议中的窗口字段来实现的。这个窗口字段指示了发送方当前可用的缓冲区大小,也就是可以发送的数据量。接收方通过发送确认报文来告知发送方它当前所能接收的数据量,即接窗口大小。
滑动窗口本质是降低了确认应答机制中等待ACK的时间..集体的措施就是:批量发送,批量接受
把多份等待时间合并成一份(不等待的批量发送数据,使用一份的等待时间来等待一组数据的多个ACK),把能不用等待就能直接发送的数据的最大的量,称为"滑动窗口的大小"
滑动窗口的机制使得发送方和接收方能够根据网络状况进行动态调整,以实现流量控制和拥塞控制。通过合理设置窗口大小,可以避免发送方发送过多的数据导致接收方无法及时处理,同时也可以防止网络拥塞的发生。
操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从缓冲区删掉。
丢包
1.ACK丢失
ACK丢失后,不需要做任何的处理,在之前讲给数据编序号时,序号的定义就是,表示从序号往前所有的数据都确认到达了.也就是当收到2001这个ACK时,代表是1~2000就都传输成功了,2001这个ACK涵盖了上一个1001的ACK的信息
2.数据
5.流量控制
流量控制是一种干预发送窗口大小的机制,滑动窗口越大,传输效率越高,但是窗口也不能无限大,发送方的传输速率不能超过接收方的接受能力。否则可能出现接收方丢包还得重传,影响到TCP核心目的:可靠。窗口太大,完全不等待ACK,可靠性失去保障,一直重传会影响效率。
n'n'h
流量控制就是根据接收方的处理能力协调发送方的发送速率
如何告诉发送方窗口大小呢?
当A给B发送一个数据后,将B的处理能力看作一个水池,B会计算剩余空间,将这个值通过ACK返回给发送方,A就根据这个值来确定发送速率,也就是窗口的大小
16位窗口字段,就是存放了窗口 大小信息(报文是ACK的时候,这个字段才有效),16位是否意味着窗口最大是64kb呢?其实不是这样,TCP为了扩大窗口,在选项部分引入了窗口扩展因子.如果窗口大小是64,扩展因子为2.那么64<<2=256kb.
滑动窗口大小是不固定的随着传输过程动态调整。
当窗口大小为0,就要停止发送,等待过程中,会给接收方定期发送窗口探测报文*(不携带任何业务数据),为了触发ack查询窗口大小。
TCP中有应用程序和系统内核。当数据到达接收方的系统内核中,tcp socket内部对象带有接收缓冲区,A——》B的数据,会先到达b的接收缓冲区,b的应用程序会调用read方法把数据从接收缓冲区读取出来,进一步处理,一旦被read就可以从接收缓冲区中删除了。
就像生产者消费者模型。
生产者a,消费者b,交易场所b的接受缓冲区,消费能力就是b的处理能力,具体去取决于b的应用程序的代码,接收方缓冲区的剩余空间越大,处理能力就越强。
6.拥塞控制
滑动窗口大小=MIN(流量控制和拥塞控制)。
流量控制考虑接收方的能力;拥塞控制考虑传输过程中中间节点的处理能力。
网络上有大量的计算机,也传输着大量的数据,网络状态被来就很拥堵,不清楚网络的状态情况下,发送大量的数据,就会引起严重后果,只要有一个节点达到处理能力的上限,就会影响到可靠传输。
TCP引入 慢启动 机制,先发少量的数据,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。(敌进我退,敌退我进,发送方不断调整窗口大小进行试验)。
慢启动只是初始比较慢,增长速度非常快
- 拥塞的窗口:窗口大小,传播速度;传播伦次:当前TCP第一次发送数据。
- 指数增长->线性增长,传输速度也越来越快,then丢包(网络拥塞),一旦出现丢包就把窗口缩小,重新进行前面的慢启动-指数-线性,并且根据当前丢包的窗口大小,重新指定线性增长阈值。
- 少量丢包触发超时重传,大量的丢包触发拥塞控制。
- 拥塞控制就是TCP尽可能将数据尽可能快的传给接收方,但避免速度太快造成网络拥塞造成丢包的折中方案。
7.延时应答
延时应答也是提升效率的机制。——>增大窗口大小
在流量控制那块就是根据缓冲区剩余空间大小来决定发送速度即窗口大小,如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小.如果收到数据之后,不是立即返回ACK,而是等待接收方消耗一些数据,剩余空间就更大了,返回的窗口就更大了。
sum:延时返回ack,给接收方更多时间,来读取缓冲区数据。缓冲区剩余空间越大,即窗口越大,网络吞吐量就越大,传输效率就越高。
但是延时应答也有限制
数量限制:每隔N个包就应答一次(一般取2)
时间限制:超过最大延迟时间就应答一次(一般200ms)
8.捎带应答
用于在发送数据包时同时携带其它的信息。通常情况下,数据包中会包含应答ACK(Acknowledgement)信息,来确认之前已经接收到的数据包。而捎带应答则是利用这个ACK信息的空余空间,来携带其它信息。如三次挥手中第二次就携带了ack+syn(同步报文段),在一些高延迟、高丢包的网络环境中,捎带应答可能会导致额外的问题,如增加网络拥塞和降低网络吞吐量等。
9.面向字节流
在TCP连接中,数据被看作是一个连续的字节流,而不是被分割成独立的消息或数据包。
而UDP的接收缓冲区是一个接一个的数据报,这样应用程序读取时就知道那里到哪里是一个完整的数据。TCP面向字节流的特性意味着数据在发送端被分割成小的数据段,并在接收端重新组装。TCP协议负责将数据流切分为适合网络传输的数据段,并确保这些数据段按序到达接收端,并进行正确的重组。
如果有多个应用层数据包同时被传输过去,就会出现粘包问题。接收缓冲区中的数据就以字节形式仅仅连接在一起,接收方的应用程序读取数据时就可以一次读1个,2个,......,N个,但是最终的目标是读取到完整的应用层数据包,问题:接收方的应用程序并不知道从哪到哪是一个完整的应用层数据包。
核心思路:定义好应用层协议,明确应用层数据包之间的边界。
1.引入分隔符;2.引入长度;在应用层协议的格式中常见的json,xml,protobuffer都明确了边界。
TCP粘包问题主要有两种情况:
- 粘包:多个数据包被接收方一次性接收到,形成了一个粘包。这种情况下,接收方需要额外的处理来正确地拆分和解析每个数据包。
- 拆包:一个数据包被拆分成多个片段发送,接收方需要根据片段的顺序和边界重新组装数据包。
造成TCP粘包问题的原因主要有以下几点:
- 发送端连续发送:如果发送端连续发送多个数据包而接收端没有及时读取,这些数据包就会被接收端一次性接收到,形成粘包。
- 缓冲区大小限制:TCP接收端的缓冲区大小有限,当接收端缓冲区无法容纳整个数据包时,数据包会被拆分成多个片段进行传输,从而产生拆包问题。
- 延迟确认机制:TCP的延迟确认机制可能会导致多个数据包被一起确认,从而产生粘包。
为了解决TCP粘包问题,可以采用以下方法:
- 显式消息边界:在传输的数据中加入消息边界标识,如特殊字符或长度字段,以便接收方可以准确地切分每个数据包。
- 消息长度字段:在发送数据时,将数据长度作为一个固定长度的字段添加到数据包中,以便接收方可以根据长度字段来正确解析每个数据包。
- 使用定长数据包:将发送的数据固定为固定长度的数据包,无论实际数据长度如何,都填充到固定长度,这样接收方可以按照固定长度来解析每个数据包。
- 清空缓冲区:接收端可以及时读取和处理数据,尽快清空接收缓冲区,减少粘包和拆包的可能性。
10.TCP异常情况
传输过程中出现了不可抗力导致TCP异常
进程终止:进程终止会释放文件描述符,相当于调用socket.close(),触发FIN,进行四次挥手。和正常关闭没有什么区别。TCP的连接管理独立于进程而存在
机器重启/关机:触发强制终止进程,和进程终止的情况相同
机器掉电/断网: 来不及杀进程,来不及发送fin。接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会 进行reset(重置)。即使没有写入操作,TCP自己也内置了一个保活定时器(心跳包),会定期询问对方是否还在。如果对方不在,也会把连接释放。
A->B:
A:超时重传——连接重置——单方面断开;
B:心跳包——对方无响应——单方面释放连接;
TCP&UDP
连接性:
- TCP是面向连接的协议,通过三次握手建立连接,确保通信双方能够可靠地传输数据,并进行连接的关闭。
- UDP是无连接的协议,不需要事先建立连接,直接将数据包发送给目标地址。每个数据包都是独立的,没有顺序要求。
可靠性:
- TCP提供可靠的数据传输,通过序列号、确认应答、超时重传等机制来确保数据的可靠性。如果数据包丢失或损坏,TCP会自动进行重传。
- UDP不提供可靠性保证,数据包一旦发送出去就无法得知是否到达目标。它适用于对实时性要求较高的应用,如音频和视频流。
传输效率:
- TCP通过流量控制、拥塞控制等机制来提供高效的传输服务。它可以自动调整发送速率和窗口大小,以适应网络的变化。
- UDP没有流量控制和拥塞控制机制,发送端会尽可能快地将数据发送出去,适用于对实时性要求较高、容忍丢包的应用。
数据大小限制:
- TCP将应用层数据按照最大传输单元(MTU)进行分段,可以传输任意大小的数据。TCP会确保数据的完整性和有序交付。
- UDP每个数据包有固定的大小限制,最大长度为64KB。如果应用层数据超过了这个限制,需要进行分片和重组。
应用场景:
- TCP适用于对数据可靠性和顺序性要求较高的应用,如网页浏览、文件传输、电子邮件等。
- UDP适用于对实时性要求较高、如音频和视频流、在线游戏,局域网内部的主机之间的通信等。它在传输速度和实时性方面更加高效。