TCP协议
TCP,即Transmission Control Protocol,传输控制协议.
TCP协议数据格式
- 16位源端口号与16位目的端口号表示数据从那个进程来要到那个进程去.
- 32位序号表示一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号(TCP将每个字节的数据都进行了编号,称为序列号).
- 32为确认序号表示接收方对发送的tcp报文段的响应。 其值是收到的TCP报文段的序号值加1.
- 4位TCP报文长度表示该TCP头部有多少个32位bit(有多少个4字节),所以TCP头部最大长度是15 * 4 = 60字节.
6位标志位:
URG:表示紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:表示复位连接,也就是重新建立连接
SYN:表示同步,把携带SYN标识的称为同步报文
FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
- 16位窗口大小:表示接收端TCP 能最大提供65535个字节的缓冲.
- 16位校验和:发送方通过以TCP报头与报文数据进行一定数学计算得到的结果填充于此,接收方以相同数学方法进行计算,如果校验和相同则校验成功.
- 16位紧急指针:标识哪部分数据是紧急数据.
TCP原理
确认应答
接收到发送方的数据后,会返回发送一个带有接收数据的最后一个字节的下一个字节序号的ACK报文.
比如接收到的最后一个字节序号为1000,则返回的序号为1001,代表前1000个字节数据已经全部收到.
为解决后发先至,tcp会有一个接收缓冲区(内核中的内存空间),每个socket都有一份自己的接受缓冲区,tcp可在缓冲区对收到的消息根据序列号进行整队.
超时重传
网络传输可能会出现丢包的情况:
- 发送的数据报接受方没有收到
- 接受方收到后,发回的ack报文传输过程中丢失.
应对策略
发送方没有收到ack应答报文,就会隔一段时间进行报文重发,随着重发次数的增加,间隔时间也会增长,连续多次重传后还是无法获取ack报文,此时tcp就会尝试重置连接,如果重置失败,则会释放连接(TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间).
如果收到重发数据,数据重复,tcp会根据收的数据序号自动进行去重
问tcp如何实现可靠性的?
答:确认应答+超时重传
连接管理
三次握手
上述过程由内核自动完成,应用程序干预不了.
握手☞的是通信双方进行一次网络交互,相当于客户端与服务器端,通过三次交互,建立了连接(双方各自记录对方的信息)
三次握手的目的就是为了投石问路,验证客户端和服务器端,各自的发送能力与接受能力是否正常!
客户端向服务器发送SYN报文问服务器你准备好了没,服务器返回SYN+ACK报文说我准备好了,你呢?客户端发送ACK告诉服务器我准备好了,到此连接建立成功.
四次挥手
进行断开连接,断开连接服务器与客户端都有可能先进行发起FIN结束报文.
为什么是四次挥手而不是三次挥手呢?
ack与fin有一定概率合并成为一个的!!但通常情况下不能合并!
ack与fin是不会同时触发的,因为ack是系统内核完成的,而fin而是由应用程序来进行控制的,调用socket的close方法才会触发fin!!!
滑动窗口
发送方一次发多条数据到一定程度停止发送,发送方一次等待多个ack,收到一条ack报文发送一条数据,形似一个滑动的窗口.
出现丢包情况:
- 数据包到达,ACK报文丢失
这种情况没啥影响,只要最后几个ack报文收到,就表明前面的ack也都收到了(确认序号的含义表示的是该序号之前的所有数据都已经收到). - 数据包未到达
如果是这种情况,应答报文一直显示已经接受到了1001,1001到2000会超时重传,而后面数据的接受是不会受影响的,当1001到2000接受到后,会直接返回已接受数据的最后一个字节的下一个字节的序号.
上述超时重传称为快述重传.
流量控制
本质就是让接收方来限制一下发送方的速度-根据接收端的处理能力,来决定发送端的发送速度.
当ACK报文头部的ack位为1时,报头的窗口大小字段就会生效,返回一个窗口大小来决定发送方发送的速度.
当发送方,发现对方接收缓冲区满了之后,就会暂停发送,但仍然会每隔一段时间触发一个"窗口探测"报文,如果探测一会发现窗口大小不为0,则继续发送.
拥塞控制
TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据.
衡量了传输路径的处理能力–木桶效应(传输路劲上某一个路由器可用传输速率很小,那么这个路由器的传输能力即为传输路径上的最大传输能力)
通过实验的方式,找到一个合适的发射速率–先开始以一个较小的速率发送,不丢包的情况下提高速率,如果丢包,立即调小速率. 达到一个动态平衡.
实际发送的窗口大小=将拥塞窗口和接收端主机反馈的窗口大小(流量控制窗口)做比较,取较小的值作为
实际发送的窗口
此图描述: 拥塞窗口先以一个较小值进行传输,无丢包情况下速率开始以指数形式增长,达到一定阈值后以线性进行增长(避免一下子超过上限很多),当开始出现丢包情况时认为当前窗口已达路径上限,且窗口回到一个较小值,阈值更新为为上限的一半;
延迟应答
延时应答的核心在于让ACK应答报文稍微延时一会儿应答,那么此时应答报文中的接受方缓冲区大小大概率比立即返回大一点(应用程序一直从接收缓冲区读取数据),此时发送方可发送的数据就会多一点.
窗口越大,网络吞吐量就越大,传输效率就越高,在保证网络不拥塞的情况下尽量提高传输效率
数量限制:每隔N个包就应答一次;
时间限制:超过最大延迟时间就应答一次;
捎带应答
接收方的应答报文ack稍等一会接收方的响应报文 二者合并为一个数据报进行发送可提高效率.
捎带应答的效果----四次挥手有可呢三次挥完.
面向字节流
粘包问题
A连续给B发送了多个应用层数据报,这些数据就会堆积在B的接收缓冲区,此时B的应用程序再去读这些数据报就会很难分清楚那个是一个完整的数据报,很容易读半个(在TCP的协议头中,没有如同UDP一样的 “报文长度” 这样的字段,站在应用层的角度,看到的只是一串连续的字节数据,不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包).
解决方法:
- 约定分割符.
- 约定长度(例如约定数据的前四个字节,用来表示数据报的长度).
异常问题
- 进程关闭/进程崩溃
进程没有了,socket是文件,随之关闭,虽然进程没有了,但是连接还在,可进行正常四次挥手. - 主机关机
所有用户进程被关闭,触发四次挥手,如果没有挥完,比如服务器端发来fin ,客户端没来及ack就关机了.
此时对端就会重转fin…重传几次后发现没有ack后尝试重置连接,没有成功后就释放连接. - 断电/断网
- 如果对方是发送方
对方就会收不到ack=>超时重传->重置连接->释放连接 - 如果对方是接受端
对方是无法立即知道,你这边是没来得及发数据还是直接没了
这使tcp的心眺包 保活机制就体现作用了
- 如果对方是发送方
接收方定期给发送方发送一个心跳包如果有响应就说明对端还存活,如果没有响应而说明对端已经挂了(心跳包时周期性的并没有那么及时,并且判断也没有那么严格).