路漫漫其修远兮,吾将上下而求索
文章目录
一、TCP报头结构
二、TCP确认应答机制
我们知道TCP的特点:
有连接传输
面相字节流
提供可靠传输
全双工
有接收缓冲区和发送缓冲器
那么TCP中是如何保证数据的可靠性呢??(尽可能的传过去 不管成功不成功都有个应答)
核心机制就在 接收方收到或者没收到 都会有个应答 这就是TCP 的确认应答机制
为了解决上述的先发后至的这种问题 TCP中使用确认序号来保证数据传输的前后顺序
TCP会针对每一个字节来编号 比如说 这里发送的在不在? 占有1000字节 那么前1000字节就是数据 那么你前面发来的数据就必须是<1001序列号的数据 以此类推
那么网络中为什么会出现先发后至的情况呢?
因为网络中的数据 不一定都是直接到达目的地的 往往要经过很多的设备进行转发 这就好比结婚时候的车队 比如说这个车队有1000辆车 一整个车队就是你的数据 当开始的时候 所有的婚车都开了 他们不能保证还是按照原来的顺序挨个排列到我的目的地 往往是一出发 就各走各的 到了目的地再整合一下队伍 TCP上的数据也是这样传输的 我们不管数据是怎么传输的 只要到接受缓冲器后 按照我们的序列号进行1-1000的排序 等到所有数据都到了之后 才是正式到达
三、超时重传
当我们的前面的传输没有问题之后 ,如果遇到了丢包?那我的客户端或者服务器就有一方收不到ack或者是数据 这时候就造成了数据丢失 这样就叫做丢包
那么TCP是如何解决丢包问题的呢?? TCP使用超时重传来解决丢包问题
丢包我们可以分为两种
{
1.一种是我的接收方没有收到数据 自然不会返回ACK
2.是我的接收方收到了数据 但是返回去的ACK丢失了
}
如果是第一种情况 那么我们的超时重传就是 我的接收方发送了数据 到了一个特定的等待时间 还没有收到ACK 此时发送端就会再次重新发送这个数据 (总不能一直发一直丢吧TnT)
如果是第二种情况 此时收不到ACK 超过等待时间 也会对数据进行重发 一直到收到ACK 此时虽然我们的接收方可能会收到多条数据 但是我们的TCP十分的智能 TCP会根据你的序列号自动去除重复的数据~
上述的等待时间 一般来说是TCP动态计算这个等待时间
在Linux中超时等待时间是以500ms为一个单位 如果第一次重发还是得不到回应 那么就等待2*500ms 以此类推 当达到一定的重传次数还是得不到回应 此时就会强制关闭连接
四、连接管理
1.TCP三次握手(建立连接)
为什么要三次握手??? 其实就是检查一下当前接收方和发送方的接收功能和发送功能是否正常!!
2.TCP四次挥手(断开连接)
四次挥手就是通信的双方各自给对方发送FIN(结束报文)再给双反返回ACK(应答报文)
五、滑动窗口
对于我们的TCP来说 我们使用确认应答来保证可靠传输 但是这样就会导致一个问题:
我的数据发出去 我需要等待ACK确认收到了 才会发送下一个数据 这样就导致我大部分时间都在等ACK返回 效率非常低!!! TCP就使用滑动窗口来优化这个效率问题~
滑动窗口也非常简单 我们之前是一次发一个数据 等待一个ACK 我们可以一次发多个数据 等待多个ACK返回 而且不用等到全部的ACK返回 只要有一个ACK返回 那么我就可以接着发送下一个数据~ 这样效率就大大提高了 这里说的一次发多少数据 就是窗口大小
举例: 比如说 我们一次发四条消息(窗口大小) 然后等待ACK 只要有一个ACK返回 那么我就可以发送第五个数据了(不需要等全部ACK返回)
这样我们就可以一次发多条数据 相比之前效率就大大提高了
为了维护这个窗口 TCP使用接收发送缓冲区来记录当前的哪些数据有应答了 那些还没有应答 只有确认得到应答 才会从缓冲区删除这个
如果这个时候出现丢包怎么办呢????
1.返回的ACK丢了 数据到达了
这里我们注意 我们的应答报文ACK 返回的时候 如果返回的是5001-6000 那么就说明 我前面的1-6000的数据是一定已经收到了的 是个包含关系
比如说这这里(窗口大小为6) 我们的1001的ACK丢了 但是我的2001的ACK发送方收到了 就说明我的1001确认也是已经收到了的
3001和4001的ACK丢了 但是收到了5001的ACK 就说明前面1-5001的数据是已经收到了 ~ 所以说前面的ACK丢了没关系 只要最后一个收到了就说明前面都收到了
如果最后一个ACK丢了 那么就超时重传咯~(在服务器去重之后(发送方超时重传了)再次发送ACK给发送方)
2.发送的数据丢了 自然也不会有ACK
发送方这时候会根据确认序号来确认的我的下一个数据应该是什么
比如说我的1001-2000的数据丢了 我的接收方按照序号来找 下一个数据应该是1001 但是一直没有来 这时候就会连续问三次 (返回三个ACK 要1001-2000的数据) 如果三次还没得到我这个数据那么发送方就会重新发送这个数据 , 这里注意 我的接收方在要1001-2000的数据的时候 也在接收发送方的其他数据,放到接收缓冲区里 当我的1001-2000的数据来了之后 就可以把接收缓冲区的数据全部拿出来了了 就到了7001这个数据这里~
上述的重传 没有任何的冗余操作 整体速度比较快 这个重传过程就叫快速重传
六、流量控制
TCP用来衡量接收方的接收能力
接收端的处理速度是有限的 当我们发送方如果发的太快 接收方处理不过来 放在接收缓冲区 当接收缓冲区满了 如果再发送就会造成丢包了 TCP就使用流量控制来找到接收方的处理能力的速度 这个就叫流量控制
具体就是:接收方会在返回ACK的时候 将自己缓冲区的大小(TCP首部中的窗口大小)和ACK一起返回给发送方 发送方根据返回的这个缓冲区的大小来调整自己的发送速度
如果接收方的接收缓冲区满了 就会给窗口大小设置为0 让发送方停止传输 ,停止之后 发送方会隔一段时间发一个窗口探测的包(是为了防止接收方返回的ACK丢了 导致传输一直停止),当探测回来窗口大小不为0了(应用程序会消费缓冲区的数据) 那么就可以重新根据缓冲区大小来发送数据了
七、拥塞控制
TCP 用来衡量中间结点的传输能力 ~
在网络传输中往往会经过多个设备进行转发 每个设备都有转发的瓶颈 一个系统的瓶颈往往就是所有设备中的最垃圾的设备的瓶颈 TCP通过实验的方式 来找到这个瓶颈 以防超过瓶颈出现问题
慢启动机制:
一开始使用一个较小的值(比如说这个图使用2)作为窗口发送 如果不会发生网络拥塞 那我下一次就按照指数级增长 使用4为窗口 下一次还不网络拥塞 那我就再增长 一直到一个ssthresh 阈值 (新的ssthresh阈值就为网络拥塞的时候的窗口大小的一半)到了阈值之后再增长就是按照线性增长(这样可以即使超过了网络瓶颈 也不会一下子超出很多)当拥塞窗口到24的时候发生丢包了 那么就再次回到较小的值 2重新循环执行
八、延迟应答
当我接收方收到数据立即返回ACK 这时候返回的窗口可能比较小 我们的延迟应答就是接收到数据的时候 不立即返回ACK 而是等待一个时间后再返回ACK 这样有啥好处呢?
我们知道窗口越大 传输的效率相对也就越高 当我们等一会再返回ACK 的时候 我们的应用程序是一直在消费的 等着一会可能应用就消费了很多数据 这时候在返回ACK 他的窗口一定是比之前要大不少的 这样就可以相对提高了效率~
一般来说 我们是每隔多少包就应答一次(一般是2) 或者有个超时时间 超过这个时间再应答 (一般是200ms)
九、捎带应答
一般的客户端服务器都是一问一答情况 一般来说ACK是立即返回 但是 ACK可以延迟应答 如果在延迟应答的这个时间里 响应处理完了 就可以捎带上响应一起返回 这样就提高了效率
十、面向自字节流
TCP 是面相字节流传输的 这时候就会有粘包问题:
当我们的消息到达应用程序的时候 此时已经没有序号和确认需要来区分多个消息了 他们都是紧紧的连在一起的 我们无法区分一条消息的头和尾
怎么解决粘包问题??
1.使用特点的分隔符来分割消息 (程序员自己来搞的 只要确保分隔符不和正文冲突就行)
2.使用定长的数据来发送 我们只要保证每次读取都按照固定大小读取就行
3.使用数据的前几个字节来存储数据报的长度 这样来区分多条消息
十一、异常情况
当客户端或者服务器以非正常情况关闭 TCP是如何处理的
1.进程关闭/进程崩溃 :进程没了 但是Socket 是文件 也就随之关闭了 但是虽然进程关闭了,但是网络的连接还在 依然可以四次挥手正常断开连接
2.主机关机(正常关机):会先杀死所有的用户进程 因为要关机了 也会触发四次挥手 但是可能还没挥完就关机了 比如说这时候客户端发开FIN 咱还没来得及返回ACK就关机了 ,此时客户端就会重传FIN 如果还没有应答就会重置连接 如果还不行就断开连接
3.主机掉电/拔网线 : 这种情况是瞬间就关了 来不及做出任何动作 TCP内置了一个心跳包机制 也就是对端会定时发送一个心跳包(ping )如果此时咱的连接还在就会回同一个(pong) 来确认我是不是还正常活着 如果一方发送了心跳包 没有及时得到回应 就会认为对端挂了 也就会断开连接了~
以上就是本文的全部内容了,如果有任何问题欢迎私信改正或交流哦~欢迎大佬们.感谢您的支持