源/目的端口:表示数据从哪个进程发送,发送到哪个进程去
32位序号:发送的数据按照一个字节一个编号存放进去
32位确认号:用于给对方的响应,值为收到TCP报文段的序号值加1
4位TCP报头长度:表示TCP头部有4个字节(32位),所以TCP头部最大长度为15*4=60
6个boolean值标志位:
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接,把携带RST标识的称为复位报文段
SYN:请求建立连接,把携带SYN标识的称为同步报文段
FIN:通知对方,要关闭连接了,把携带FIN标识的称为结束报文
16位窗口大小:进行流量窗口控制
16位校验和:检验数据是否一致
16位紧急指针:标识哪部分数据是紧急数据
2. TCP协议原理
TCP协议是对数据传输提供的一个管控机制,主要体现在可靠和效率两个方面,即在保证数据可靠传输的情况下尽可能的提高效率
2.1 可靠传输机制
2.1.1 确认应答机制
向对方发送一个数据报,对方要返回一个确认应答的数据报
实现的方式:
说明:
发送的数据是基于TCP报头中的“32位序号”来保存的,一个字节对应一个序号
确认应答的数据是基于TCP报头中的“32位确认序号”来保存的,ack(确认信息)标志位置为1,返回某个序列号,说明某个序列号之前的数据全部接收到
有了确认应答,它才可以继续发送后边的数据
2.1.2 超时重传机制
发送的数据报可能因为网络拥堵等原因,超过一定时间,还没有收到确认应答的数据报,就需要重新发送
没有收到确认应答,可能是因为发送数据时候就已经发生了丢包
也可能是因为ACK丢包了
这种情况,主机B可能会接收到许多重复的数据,基于TCP协议报头中的“32位序号”可以识别到哪些包是重复的包,并把重复的包丢掉,达到去重的效果
超时时间如何确定?
如果超时时间设置的太长,会导致重传的效率
如果超时时间设置的太短,会导致频繁发送重复的数据
因此TCP协议为了保证在任何环境中都能有较高性能的通信,系统会动态的计算这个超时时间
超时以500ms为一个单位,每次判定超时重发的时间都是500ms的整数倍
重发一次,仍然不能收到应答,等待2500ms后再进行重传
仍然等不到应答,等待4500ms进行重传,以此类推,以指数形式增长
累积到一定重传次数,TCP协议认为网络或者对端主机出现异常,强制关闭连接
2.1.3 连接管理机制(三次握手,四次挥手)
真正发送数据之前,要先通过三次握手建立连接,不需要发送数据了,通过四次挥手断开连接
三次握手
客户端向服务端发送SYN,申请建立客户端到服务端的连接
服务端返回ACK(第一次SYN的应答)和SYN,申请建立服务端到客户端的连接
客户端收到数据,状态置为ESTABLISHED,表示客户端到服务端连接建立完成,并且发送ACK(第二次SYN的应答),服务端收到数据,状态置为ESTABLISHED,表示服务端到客户端的连接建立完成
四次挥手
说明:关闭的时候服务端申请关闭或者客户端申请关闭都可以
客户端发送FIN到服务端,申请关闭客户端到服务端的连接
服务端收到FIN状态置为CLOSE_WAIT,并返回ACK应答(这个动作是系统实现TCP协议栈默认执行的,不需要程序来调用代码)
服务端发送FIN到客户端,申请关闭服务端到客户端的连接(程序手动调用socket.close发送)
客户端收到FIN返回ACK应答,并进入TIME_WAIT时间等待状态,客户端等待一段时间后,状态置为CLOSED,服务端收到应答后,状态置为CLOSED
思考:
为什么服务端不将ACK和FIN合并一起发送,形成三次挥手呢?
答:因为服务端状态置为CLOSED_WAIT说明服务端准备关闭连接,但是服务端可能还在继续发送数据,得处理完之前的数据,还可以执行一些关闭连接前的工作,如消耗资源等 ,所以是先给客户端发送应答,再处理完数据再向客户端发送FIN申请关闭连接
为什么客户端要等待一段时间状态才置为CLOSED,而不之间将状态置为CLOSED?
答:如果客户端给服务端的ACK丢包后,服务端得重新给和客户端发送FIN,此时客户端得给服务端应答,所以此时状态不能置为CLOSED,得等待一段时间确保服务端收到客户端的应答
2.1.4 流量控制
接收端主机处理数据的速度有限,如果发送端发送数据太快,导致接收端缓冲区被填满,这时,发送端继续发送数据的话就会造成丢包,继而引起丢包重传等一些列连锁反应,因此TCP协议根据接收端接收数据的能力,来决定发送端发送数据的速度,这个机制就叫作流量控制
接收端将自己剩余缓冲区大小存入TCP头部中的“16位窗口大小”字段 ,通过ACK通知发送端
窗口大小越大,说明网络吞吐量越高
发送端根据接收到这个窗口的大小,控制自己的发送速度
如果接收缓冲区满了,就会将窗口设置为0,这时,发送端不在发送数据,而是定期的发送一个窗口探测数据段,让接收端将窗口大小告诉发送端
2.1.5 拥塞控制
刚开始,发送端网络状况不明,如果贸然发送大量数据,就会造成大量丢包,所以TCP协议引入慢启动的方式,先发少量数据探探路,再决定按照多大速度发送数据
此处引入拥塞窗口,刚开始时,拥塞窗口设置为1,每收到一个ACK时,拥塞窗口加1,每次发送数据的时候,拥塞窗口和流量窗口的较小的值作为实际发送的窗口,即滑动窗口的大小
注意:上述增长方式是指数级别的,“慢启动”只是开始时慢,但是增长速度非常迅速
拥塞窗口变化的方式
为了不增长那摩快,引入一个慢启动的阈值,当拥塞窗口的大小超过了这个阈值,不在按照指数方式增长,而是按照线性方式增长,如下图所示:
开始时,慢启动的阈值为窗口的最大值
网络拥塞时,拥塞窗口置1,慢启动阈值变为拥塞窗口/2,重新开始增长
2.2 效率机制
2.2.1 滑动窗口
前面的确认应答机制指出,对每一个发送的数据都对应有一个ACK确认应答,这样采取一发一收的方式有一个很大的缺点就是效率太差,为了提高效率采用滑动窗口,即一次性发送多个数据
窗口大小:指无需等待而可以继续发送数据的最大值,上图的窗口大小为4000个字节(四个段)
具体如何设置窗口大小:min(流量窗口的大小,拥塞窗口的大小)
窗口如何滑动:接收到的ACK下一个是n,滑动到n-1的位置
操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只有应答过的数据才能从缓冲区中删掉
如果出现了丢包,如何确保可靠传输?
情况一:数据已经收到,返回的ACK应答丢包
这种情况下,部分ACK丢了不要紧,因为可以通过后续的ACK进行确认
情况二:发送数据的时候就已经丢包
说明:
当1001~2000这段报文丢失后,发送端一直会收到1001这样的ACK
如果发送端主机连续三次收到相同的ACK如1001应答,那发送端主机就会重新发送1001~2000数据
此时,接收端收到10012000数据后,再次返回的ACK应答就是7001了,因为20017000数据都已经接收到了,被放到接收端操作系统内核的接收缓冲区了
这种机制,即时不超时也会发生重传,称作“高速重发控制”也叫“快重传机制”
2.2.2 延迟应答
如果接收端主机接收到数据时,立刻返回ACK应答,这时候返回的流量窗口就比较小,但是流量窗口越大,网络吞吐量越大,传输效率就越高,所以等待一部分时间,待接收端处理完一部分数据 ,就可以将流量窗口设置为大一点的值,这样网咯吞吐量大,效率高
延迟是为了高吞吐量,但是也不能无限延迟
数量限制,每隔n个包就应答一次
时间限制,超过最大延迟时间,就应答一次
具体的数量和时间,不同操作系统有差异,一般n取2,超时时间取200ms
2.2.3 捎带应答
服务端接收到客户端的消息后,要返回一个ACK有时候可能需要返回一个响应,此时ACK可以搭载顺风车,和响应合并为一个数据返回,不用再将ACK和响应数据分开发送
如何解决粘包问题?明确包的边界
对于定长的包,每次都按照固定大小读取即可
对于变长的包,可以在包与包之间明确分隔符(应用协议,程序员自己定,只要保证分隔符和正文不起冲突即可)
4. TCP的异常情况
进程终止:进程终止会释放文件描述符,仍然可以发送FIN。和正常关闭没有什么区别
机器重启:和进程终止的情况相同
机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了就会进行reset,即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在,如果对方不在,也会把连接释放
另外,应用层的某些协议,也有一些这样的检测机制,例如HTTP长连接中,也会定期检测对方的状态,例如QQ,在QQ断线之后,也会定期尝试重新连接
5. TCP协议特点总结
有连接:通过三次握手建立连接后才可接发数据,TCP协议是全双工的,即每端既可以发也可以收
可靠传输:网络数据传输是一跳一跳的,经过路途中的设备可能发生数据丢失,可靠传输是可能发生数据丢失但有机制保证对方能接收到
面向字节流:可以多次的收发数据(连接没有关闭时,可以多次的接收和发送数据)
有接收缓冲区和发送缓冲区:发送数据时,是先写到发送缓冲区,再刷新缓冲区(flush)
大小不受限制:多次的收发数据,每次的数据可以很大
6. 基于TCP的应用层协议
HTTP
HTTPS
SSH
Telnet
FTP
SMTP
也包括自己写TCP程序时自定义的应用层协议