目录
一、TCP协议报文
1、32位序号/32位确认序号
给报文的每个字节进行标记,作用在下文中体现~
2、4位首部长度/保留6位
首部长度即报头长度,单位是4个字节,所以报头最大长度为15*4=60b。
保留6位:留下一个可扩展的空间,以备后用。
3、六位标志位
·URG:表示晋级指针是否有效;
·ACK:表示确认号是否有效;
·PSH:提示接收端应用程序立刻从TCP缓冲区读走数据;
·RST:对方要求重新建立连接,把携带RST标志的称为复位报文段;
·SYN:请求建立连接,把携带SYN标志的称为同步报文段;
·FIN:通知对方,本端要结束连接,把携带FIN标志的称为结束报文段。
每个标志位中0表示无效,1表示有效。
4、选项
选项部分可有可无,可以有一个,也可以有多个,选项最多占用40个字节。
其他未介绍的字段会在下文中体现~
二、TCP协议的机制
TCP协议有很多特点:其中有连接、面向字节流、全双工的特点在代码中都有所体现,而可靠传输的特点则需要下文中的前三个机制来实现:
1、确认应答机制
假设主机A给主机B发送了一个数据,主机B收到该数据时会立即返回一个应答报文,也叫ACK报文,用来通知主机A,刚才发放的数据主机B已经收到了~
普通报文中的ACK标志位为0,应答报文中的ACK标志位为1~
但是网络传输环境非常复杂,很可能出现后发先至这种乱序的情况,,为了解决这种情况,就需要用到TCP报头中的32位序号和32位确认序号:
32位序号针对主机A发送的请求数据,对数据的每个字节进行编号~
32位确认序号针对主机B的ACK报文有效~
确认应答机制是保证TCP可靠传输的最核心机制!
2、超时重传机制
确认应答机制描述的是数据顺利从主机A传送到主机B的情况,但是在网络传输过程中,也可能会发生丢包的情况:
(1) 数据报丢失;
(2) 应答报文丢失。
针对上述两种丢包的情况,就需要通过超时重传机制来解决~
(1) 数据报丢失:
主机A向主机B发送的数据报如果丢失,那么主机B没有收到数据就不会向主机A发生ACK报文,主机A在等待一段时间后没有收到主机B的ACK报文,就会重新向主机B发送一次数据~
(2)应答报文丢失:
主机B收到了主机A的数据并向主机A发送了ACK报文,但是ACK报文在传输过程中丢失了,那么主机A在等待一段时间后仍然不会收到主机B发来的ACK报文,于是主机A会再次向主机B发生数据~
但是两次发送的数据重复了该如何解决呢?
由于两次数据相同,那么它们的32位序号也相同,所以主机B在收到数据后会根据序号进行去重,保证主机B的应用程序中的代码通过socket读到的不是重复的数据~
超时等待时间并非是均等的,而是逐渐变长的:
单个数据丢包的概率本身就比较小,如果出现连续多次丢包的情况,说明当前网络环境比较差,那么就没必要频繁地传输数据了~
超时重传机制并不会无限次地重传数据,如果重传多次后,仍然无法传输成功,就会尝试重置TCP连接,如果无法连接上,那么就会放弃连接~
3、连接管理机制
TCP的连接是一个逻辑上的“虚拟连接”:
假设主机A和主机B建立了连接,那么主机A和主机B的操作系统内核里,会记录一个数据结构,保存对方的IP,端口号,使用的协议等信息~
1.建立连接(三次握手):
① 主机A向主机B发起尝试建立连接的请求——SYN(同步报文段);
② 主机B向主机A返回一个ACK报文和一个SYN;
③ 主机A向主机B返回一个ACK报文。
注意:
(1) 由于主机B返回给主机A的ACK报文和SYN都是由操作系统内核立即发送的,因此将两条数据结合为一条发送;
(2) 三次握手建立连接并不传输任何业务上的数据。
如果一个报文中SYN这一位为1,说明当前报文是一个尝试和对方建立连接的同步报文段~
三次握手的作用:
(1) 检查一下当前网络是否畅通;
(2) 检查连接双方的接收数据和传送数据的能力是否正常;
(3) 三次握手的过程中,连接双方也在协商一些重要的参数。
建立连接时两个重要的TCP状态:
① LISTEN:服务器启动后,绑定了端口(new SeverSocket完成)的状态。
② ESTABLISHED:连接建立好之后的稳定状态。
2.断开连接(四次挥手):
① 主机A向主机B发送结束报文段——FIN;
FIN标志位为1,说明当前报文是结束报文段~
② 主机B向主机A返回一个ACK报文段;
③ 主机B向主机A发送结束报文段;
④ 主机A向主机B返回一个ACK报文段。
注意:
ACK报文是由操作系统内核在收到数据后立即发送的,而FIN是由应用程序代码中的socket调用close后才发送的,所以两个报文的发送时机不一样,因此ACK和FIN不能进行合并(但是也不绝对)。
断开连接时两个重要的TCP状态:
① CLOSE_WAIT:等待代码中调用close方法时的状态。
② TIME_WAIT:主动发起端口连接的一方,等待对方彻底断开连接时的状态。
以上三个机制保证了TCP协议的可靠传输,但是由于做了许多额外的工作,导致传输的效率降低,为了提高传输效率,TCP又引入了以下几种机制:
4、滑动窗口机制
没有使用滑动窗口时的传输:主机A发送一条数据后,等主机B返回一个ACK之后,再发送下一条数据……
当前这种传输方式的效率非常低:主机A的大量时间花费在等待主机B返回ACK报文上。
滑动窗口机制:主机A一次性发送一批数据,然后再等待主机B的ACK,这样就可以大大提高效率~
把无需等待确认应答就可以继续发送数据的最大值称为“窗口大小”:
上图中的窗口大小为:4000个字节。
如果在传输过程中出现丢包的情况怎么解决呢?
(1) 数据报丢失
① 如上图,序号为1001~2000的数据报在发送时丢失,那么后续的数据在被主机B接收时,主机B会不断地发送1001的ACK报文,
② 当主机A连续收到三个重复的1001ACK报文,就会重新发送1001~2000的数据;
③ 由于2001~7000的数据并没有丢失,而是被放到了主机B的接收缓冲区内,所以当主机B接收到主机A重新发送的1001~2000的数据后,再次返回的ACK就是7001~
这种机制叫做快速重传:搭配滑动窗口的超时重传。
(2) ACK报文丢失
上图中,虽然3001的ACK报文丢失了,但是后面到达的ACK报文回替3001报文进行应答,比如4001报文就表示4001之前的数据已经收到了~
所以如果ACK报文丢失的话,并不会影响到后续数据的发送,因为后面的ACK会替前面丢失的ACK进行回答~
5、流量控制机制
接收端处理数据的速度是有限的,如果滑动窗口的窗口过大,就可能导致接收端的接收缓冲区的空间被占满,进而引发丢包、丢包重传等一系列的连锁反应。
流量控制机制:根据接收端的处理能力,决定发送端的发送速度。
① 接收端通过计算接收缓冲区的剩余空间来作为发送方发送速度的参考值;
② 接收端将自己接收缓冲区的剩余空间的值放入报头的“窗口大小”字段中,通过ACK报文反馈给发送端;
③ 窗口大小的值越大,说明接收端的接收缓冲区的剩余空间越大,发送端就可以提高自己的发送速度;值越小,说明接收端的接收缓冲区的剩余空间越小,发送端就会减慢自己的发送速度;
④ 如果接收端的接收缓冲区满了,就会将窗口大小的值设为0,此时,发送方不会再发送业务上的数据,但是会定期发送一个窗口探测报文,探测接收端的窗口大小。
注意:
(1) 只有在ACK标志位为1的报文中,“窗口大小”字段才有意义,否则无效;
(2) 虽然窗口大小字段只有16个比特位,但TCP的窗口大小最大值并不是65535字节,因为报头的选项中还包含一个窗口扩大因子M,实际窗口大小 = 窗口大小的值左移M位。
6、拥塞控制机制
上述流量控制机制是通过接收端的处理能力来控制窗口大小,而拥塞控制机制则是通过发送端来控制窗口大小,实际窗口大小的值取决于它们之中的较小值。
发送端发送的数据需要经过许多网络设备才能到达接收端,但如果在刚开始不清楚网络状态的阶段,贸然发送大量的数据,仍然可能引发一些问题。
于是,TCP引入了“慢启动”机制:
① 慢启动机制引入一个“拥塞窗口”,初始值为1,发送端每收到一个ACK报文,拥塞窗口加1;
② 每次发送数据报的时候,取拥塞窗口和发送端反馈的窗口大小二者中的较小值作为实际发送的窗口大小;
③ “慢启动”机制只是初始时比较慢,但是增长速度极快,按照指数的方式增长(为了在短时间内摸清网络传输所能承载的最大值);
④ 由于指数增长方式太快,所以又引入了慢启动的阈值,当拥塞窗口的值超过阈值,就不再按照指数方式增长,而是改为线性增长。
⑤ 如果在传输过程中发生丢包,就减小拥塞窗口。
· 当TCP开始启动时,慢启动阈值等于窗口最大值;
· 在每次超时重发的时候,慢启动阈值会变为原来的一半,同时拥塞窗口置为1。
7、延迟应答机制
延迟应答机制是基于流量控制机制而引入的提高效率的机制~
如果接收端收到数据后,立刻就返回ACK,那么这个时候返回的窗口可能比较小。
但是,如果在收到数据后稍微等一会儿再返回ACK,那么接收端的应用程序在这个等待的时间内,就可以从接收缓冲区读取很多数据,此时返回的窗口大小的值就更大,窗口越大,传输效率就越高~
延迟应答机制的延迟时间除了可以用时间来衡量,也可以用传输数量来衡量:
数量限制:每隔N个包就应答一次;
时间限制:超过最大延迟时间就应答一次。
操作系统不同,具体的数量和超时时间也不同;一般N取2,超时时间取200ms。
8、捎带应答机制
捎带应答机制是基于延迟应答机制而产生的一种提高传输效率的机制~
假设主机A给主机B发送了一个数据报,主机B此时会等待一段时间再给A返回ACK报文,如果在这个等待的时间里,主机B也给主机A发送一个数据报,那么这个数据报就可以和ACK合二为一,一起发送给主机A~
因为捎带应答机制的存在,主机A和主机B断开连接时的四次挥手也可能回变成三次,但不是100%发生~
9、面向字节流
1.缓冲区
创建一个TCP的Socket对象时,会同时在内核中创建一个发送缓冲区和接收缓存区。
发送缓冲区:
调用write时,数据会先写入到发送缓冲区中;
如果发送的字节数太短,数据就会先在发送缓冲区内等待,等到缓冲区的数据达到一定长度,或者其他合适的时机再发送;
如果发送的字节数太长,会被拆分成多个TCP数据包发送。
接收缓冲区:
接收数据时,数据会先放入内核的接收缓冲区;
然后应用程序通过调用read从接收缓冲区读数据。
2.粘包问题
粘包问题:应用程序在接收缓冲区读取数据时,看到的只是一连串的字节数据,并不知道从哪到哪是一个完整的应用层数据包。
比如,接收缓冲区中现在有个“hello",应用程序可能会把"h"当作一个完整的应用层数据,也可能是"he","hel","hell","hello"当作一个完整的应用层数据。
一般面向字节流的传输层协议都存在粘包问题~
解决方法:定义应用层协议时,明确好两个包之间的“边界”。
(1) 使用一个特定的分隔符作为包的结束标记;
(2) 声明每个包的长度。
比如,在每个数据包的开头位置声明这个包的长度,读取数据的时候就读取这么长的数据~
10、异常情况处理
1.进程终止
无论是正常结束进程还是异常结束进程,操作系统都会回收进程的资源,包括释放文件描述符表。
释放文件描述符表就相当于调用了对应的socket的close方法,从而触发FIN报文,通过四次挥手的方式正常断开连接。
2.主机正常关机
主机关机的时候,会强制结束所有的进程,后续就和第一种情况一样
3.主机掉电
(1)发送方的主机掉电
①接收方不知道发送方掉电,就会先等待一段时间;
②一段时间后还没有收到数据后,就会发送一个“心跳包”;
心跳包是周期性触发的,是一个不携带任何业务数据的包;如果发送方还在的话,就会返回一个心跳包。
③如果发送方没有返回心跳包,就放弃连接。
(2)接收方的主机掉电
①发送方并不知道接收方掉电,仍然会继续发送数据;
②此时发送数据不会收到接收方返回的ACK报文,发送方便会触发超时重传机制;
③重传几次后,仍然收不到ACK,于是发送方会触发复位报文段(RST标志位)尝试重新连接;
④重新连接失败后,发送方就会放弃连接。
4.主机断网
情况同主机掉电~