TCP的报文结构中,16位源端口,16位目的端口,16位校验和和UDP是一样的
TCP的首部长度是指TCP的报头长度,TCP报头的长度是可变的,不像 UDP 就是固定 8 字节,4位首部长度指4个bite位,范围是0->15,表示TCP 报头的长度
此处TCP的6位保留位是为了以后TCP扩展用的,方便以后扩展升级,给报头添加一些新的部分
可靠传输是TCP的特点,那么TCP是如何实现可靠传输的?
确认应答是TCP实现可靠传输的最核心机制,可靠传输不是说把消息100%发送给对方(由于网络物理等因素干扰,不可能实现),而是,当我们成功把数据发送给对方,我们知道发送成功了,当我们没有把数据发送给对方,我们能知道发送失败了。
确认应答(ACK):当接收方成功接收到发送方发送的数据包时,会返回一个确认应答数据包。这个数据包里包含一个确认号(Acknowledgement Number),表示已成功接收到的数据字节序号,以便发送方知道其数据已经成功传送到接收方。
由于网络俩个主机之间路线有多条,数据报1和数据报2走的可能是不同的路线,数据报1的转发路径上的路由器/交换机和数据报2的路由器/交换机可能不一样,有的转发速率快,有的转发速率慢,此时俩个数据报到达的顺序可能就发生改变,就可能会发生后发先至这种情况,那么遇到这种情况,应该如何解决呢?
当我们使用序号和确认序号,就不怕顺序错乱了,即使顺序错乱,因为有序号和确认序号我们也不会产生歧义,真实的 TCP 数据传输也是引入了序号和确认序号
超时重传(Timeout Retransmission):TCP为每个发送的数据包设定一个超时时间,如果在这个时间内发送方没有收到接收方的确认应答,就会认为数据包丢失或出错,于是会重新发送这个数据包。超时重传机制有助于确保数据在有丢包、错包等网络问题时依然可以可靠传输。
如果包丢了,接收方就收不到了,自然就不会返回 ack.
超时重传,发送方就迟迟的拿不到应答报文等待一段时间之后,还是没有收到 应答报文,发送方就视为刚才的数据丢包了,就会重新再发一遍
如果我们发送的数据,丢包(发送发发给接受方的数据丢了)了怎么办?
丢包分为俩种情况,1是发送方发送的数据丢了,2是接受方返回的应答报文(ack)丢了,这俩种情况,在发送方的视角是分不清的,得到的结果都是发送方没有收到接受方的应答报文.所以发送方都会认为是发送的数据丢了
此时就会有TCP的重传机制,TCP有一个时间阈值,发送方发了一个数据后,就会等待ACK,此时开始计时,如果在时间阈值范围内没有收到ACK,就认为是丢包了,就会重新给接受方发送数据
当然,对于接收方发送返回的应答报文丢了,发送方重新发送数据.此时接收方就收到了2份同样的数据,这种情况会有特殊处理(如果不处理,就可能会发生一个支付请求,支付俩次这种情况),TCP存在一个"接受缓冲区"这样的存储空间(接收方操作系统内核里的一段内存),每个TCP,socket对象,都有一个接收缓存区(也有一个发送缓冲区),主机B收到主机A的数据,其实B的网卡已经读到数据了,把这个数据放到B对应的socket的接收缓冲区中(此处的接收缓冲区就相当于是一个阻塞队列),根据数据的序号,TCP很容易识别当前的俩条数据是否重复,如果重复就把后来的这份重复的数据丢了,这样就保证了read读到的数据,一定是不重复的
总结:由于接收缓冲区和序号的存在(接收的数据都是先放在接收缓冲区的,如果发生后发先至也没关系,由于序号的存在,我们很容易在接收缓冲区给数据重新排序和去重),即使数据重复了,顺序错乱了,接收方也能很好的处理.
重传的数据是否可能又丢包了?这是有可能的,因此超时重传可能会重传N次,当然N不会是一个很大的数字,当重传几次都丢包,此时再重传意义也不大了.(很可能是因为网络出现重大故障).因此当重传到一定次数后,此时就不会重传了,视为网络出现故障,接下来TCP会尝试重置连接(相当于断开连接,重连),如果还是失败,就彻底断开连接了,另外重传的时候,第一重传的时间间隔和第二次重传的时间间隔不一样,重传的轮次越大,间隔越大
TCP的可靠传输就是通过 确认应答 + 超时重传 来体现的,其中确认应答描述的是传输顺利的情况,超时重传描述的是传输出现问题的情况
握手(handshake)指的是通信双方进行一次网络交互~~相当于 客户端和服务器之间,通过三次交互建立了连接关系
TCP 建立连接: 三次握手
三次握手过程:
第一次握手:发送方发送一个带有SYN(同步)标志的数据包给接收方,请求建立连接;
第二次握手:接收方回复一个带有SYN和ACK(确认应答)标志的数据包给发送方,表示接受连接请求;
第三次握手:发送方再次发送一个带有ACK标志的数据包给接收方,确认收到接收方的连接应答。至此,双方建立了一个TCP连接。
SYN:客户端给服务器发起的建立连接请求称为SYN,也叫同步报文段
这里为什么要把中间俩次的响应和请求合并到一起?
这俩次交互的时机相同,都是操作系统内核在收到建立连接请求的时候,返回响应的同时也会给对方发送一个建立连接的请求 .另外,每次交互都需要封装和分用,交互成本很高,我把俩次交互合并成一次这样只用分装分用一次就可以了.
俩次握手能否完成建立连接的过程?
如果只有俩次,比如这里最后A没有给B返回一个ACK,在A的视角是可以的,知道了A保存至了B的信息,并且知道B保存了自己的信息,但在B的视角,B只知道自己保存了A的信息,但不知道A是否保存了自己的信息.另外三次握手还有个作用是,验证通信双方各自的发送能力和接收能力是否正常,如果是俩次握手,B是不知道自己的发送能力是否正常的
四次挥手:
第一次挥手:发送方发送一个带有FIN(结束)标志的数据包给接收方,请求关闭连接;
第二次挥手:接收方回复一个带有ACK标志的数据包给发送方,确认收到关闭请求;
第三次挥手:接收方发送一个带有FIN标志的数据包给发送方,表示同意关闭连接;
第四次挥手:发送方回复一个带有ACK标志的数据包给接收方,确认收到关闭应答。至此,双方断开了TCP连接。
通俗的例子:将TCP连接建立和关闭过程想象成两个人通过电话沟通。三次握手就像是打电话建立通话:A拨打B的电话(第一次握手),B接电话后回应A已经接听(第二次握手),A再次确认已经听到B的回应,通话正式开始(第三次握手)。四次挥手就像是结束通话:A表示要挂电话(第一次挥手),B确认收到挂电话的请求(第二次挥手),B也表示要挂电话(第三次挥手),A再次确认收到B的挂电话请求,通话结束(第四次挥手)。
TCP 要保证的不仅仅是可靠性,还有效率,提升可靠性,往往意味着,损失效率
此时 A 这边就花了大量的时间在等待 ACK,想提高效率,就需要缩短等待时间,批量发送数据了
这里就是批量发送 4 条数据发完之后,统一等待 ack.
每次收到一个 ack 就立即发下一条.(不是收到 4 个 ack 再发下一组)
使用一份时间,等待多个 ack总的等待时间缩短了,整体的效率就提升了
滑动窗口,批量传输,叫做滑动窗口
批量不是无限发送,是发送到一定程度就等待 ack,不等待直接发送的数据量是有上限的,而且是回来一个 ack 就立即发下一条,相当于总的要批量等待的数据是一致的.
把批量等待数据的数量,就称为“窗口大小"
确认序号的含义,表示, 该序号之前的数据都已经收到了后一个 ack,能够涵盖前一个 ack 的意思!!!
当收到 2001 这个 ack 的时候此时发送方就知道了,2001 之前的数据都收到了1-1000 这个 数据也收到了1001 这个 ack 丢了就丢了,无所谓!!!
流量控制,也是保证可靠性的机制
滑动窗口,批量发送,窗口越大,相当于批量的数据越多,整体的速度就越快
但是,是越快越好嘛?? 可靠传输!!
如果你发的太快了,瞬间把接收方接收缓冲区给打满了,接下来继续发送,此时数据就会丢包,这种情况得不偿失,还不如,发的慢点了,通过流量控制,本质上就是让接收方来限制一下发送方的速度
滑动窗口的大小 取决于流量控制和 拥塞控制,流量控制衡量了接收方的处理能力,传输路径衡量了传输路径的处理能力
拥塞控制做的事情就是衡量中间节点,传输的能力拥塞控制,是要衡量中间路径中间路径上有多少个节点?每个节点当前的情况?甚至每次传输走的路径都不同通过实验的方式找到一个合适的发送速率,开始的时候,按照一个小的速率发送如果不丢包,就可以提高一下速率(扩大窗口大小)
如果出现丢包,则立即把速率再调小动态平衡重复上述过程
延时应答能够提高传输效率,TCP 可靠性的核心,是确认应答,ACK 要发,但是不是立即发,而是稍微磨蹭一会再发,这样能够提高效率
延时应答的效果,就是通过这个延时,让接收方应用程序,趁机多消费点数据,此时反馈的 窗口大小 就会更大一丢丢
此时发送方的发送速率也就能快一些~~(同时也能满足让接收方能够处理过来)
捎带应答(Piggybacking Acknowledgement)是TCP(传输控制协议)中的一种优化策略,用于提高网络传输效率。捎带应答的核心思想是:当接收方在收到数据包时,如果有数据需要发送给发送方,那么它可以将确认应答(ACK)信息捎带在自己的数据包上,一起发送给发送方。这样,捎带应答可以减少单独发送ACK数据包的数量,降低网络拥塞和提高传输效率。
举个简单的例子:设有两台计算机A和B,A向B发送数据,同时B也需要向A发送数据。
A向B发送一个数据包;
B收到数据包,准备发送一个确认应答(ACK);
此时,B发现自己也有数据需要发送给A,于是将ACK信息捎带在自己的数据包上,一起发送给A;
A收到B的数据包,并从中提取ACK信息,确认自己的数据已成功传输到B。
通过捎带应答策略,TCP协议可以在一定程度上减少网络中的ACK数据包数量,从而降低网络负载和提高传输效率。
粘包问题,所谓的“一句话”就相当于一个“应用层数据报
当 A 给 B 连续发了多个应用层数据报之后,这些数据就都积累到 B 的接收缓冲区中,紧紧挨在一起此时 B 的应用程序在读数据的时候, 就难以区分从哪到哪是一个完整的应用层数据报.就很容易读出半个包 /一个半…
进程没了,socket 是文件, 随之被关闭,虽然进程没了,但是连接还在仍然可以继续四次挥手
先杀死所有的用户进程
也会触发四次挥手,如果挥完更好,如果没挥完,比如,对方发的 fin 过来了,咱们没来得及 ack 就关机了此时对端就会重传 fin,重传几次之后,发现都没有 ack,尝试重置连接,如果还不行,就直接释放连接
瞬间机器就关了,来不及进行任何挥手操作
1)对端是发送方
对端就会收不到 ack => 超时重传 => 重连接 =>放连接
2)对端是接收方
对端是没法立即知道,你这边是还没来得及发新的数据,还是直接没了TCP 内置了 心跳包 保活机制,虽然对端是接收方,对端会定期给咱们发一个心跳包(ping)咱们返回一个 (pong)
a)周期性b) 如果心跳没了,挂了
如果每个 ping 都有及时的 pong,这个时候说明当前对端的状态良好如果 ping 过去之后, 没用 pong, 说明心跳没了