目录
TCP报文结构
TCP的首部长度
保留(6位)
TCP特点
TCP内部的工作机制
一确认应答
超时重传
连接管理
建立建立(三次握手)
TCP断开连接(四次挥手)
TCP报文结构
TCP的报文结构中,16位源端口,16位目的端口,16位校验和和UDP是一样的,本篇文章就暂不介绍了,可参考俺之前写的UDP协议详解,
TCP的首部长度
TCP的首部长度是指TCP的报头长度,TCP报头的长度是可变的,因为在TCP报头中有选项这一栏,它是可有可无的,如果不加选项TCP报头是固定长度20字节,因此我们也可以算出选项长度:报头长度-20字节.另外注意4位首部长度指4个bite位,范围是0->15,单位是4字节,也就是说如果4位首部长度显示的是5,说明这个报头没有加选项,如果4位首部长度是6,说明报头一共24个字节,选项长度为4字节.
保留(6位)
此处TCP的6位保留位是为了以后TCP扩展用的,方便以后扩展升级,给报头添加一些新的部分,就可以直接拿这些保留位就好了
其它部分会在接下来的TCP协议工作原理中介绍.
TCP特点
有连接,可靠,面向字节流,全双工
TCP内部的工作机制
一确认应答
确认应答是TCP实现可靠传输的最核心机制,可靠传输不是说把消息100%发送给对方(由于网络物理等因素干扰,不可能实现),而是,当我们成功把数据发送给对方,我们知道发送成功了,当我们没有把数据发送给对方,我们能知道发送失败了
简单来说就是A给B发了个消息,B收到后就会返回一个应答报文(ack),此时A收到应答后,就知道刚刚发的数据已经顺利发送给了B
序号和确认序号
同样我给hxd发消息,但是我可能是连续发俩条消息,我发第二天消息的时候没有等第一条消息的ack,hxd收到我的消息就立即回应
此时就可能产生歧义,站在我的视角来看,我说走,上号,hxd的回答是滚犊子,我说"我是恁爹",hxd的回答是好啊,好啊.
由于网络俩个主机之间路线有多条,数据报1和数据报2走的可能是不同的路线,数据报1的转发路径上的路由器/交换机和数据报2的路由器/交换机可能不一样,有的转发速率快,有的转发速率慢,此时俩个数据报到达的顺序可能就发生改变,就可能会发生后发先至这种情况!此时我们就需要考虑如何规避这种顺序错乱带来的歧义.
当我们使用序号和确认序号,就不怕顺序错乱了,即使顺序错乱,因为有序号和确认序号我们也不会产生歧义
任何一条数据(包括应答报文)都是有序号的,确认序号只有应答报文有(普通报文确认序号字段无意义),如果上面的报文结构图中ACK显示的是1,则表示是应答报文,如果是0,则表示不是应答报文
TCP的序号是面向字节流的,TCP的序号是按照字节来编号的
总结,我们通过确认应答机制,通过应答报文来让发送发知道传输是否成功,进一步引入序号和确认序号,针对多组数据进行区分
超时重传
如果我们发送的数据,丢包(发送发发给接受方的数据丢了)了怎么办?
丢包分为俩种情况,1是发送方发送的数据丢了,2是接受方返回的应答报文(ack)丢了,这俩种情况,在发送方的视角是分不清的,得到的结果都是发送方没有收到接受方的应答报文.所以发送方都会认为是发送的数据丢了
此时就会有TCP的重传机制,TCP有一个时间阈值,发送方发了一个数据后,就会等待ACK,此时开始计时,如果在时间阈值范围内没有收到ACK,就认为是丢包了,就会重新给接受方发送数据
当然,对于接收方发送返回的应答报文丢了,发送方重新发送数据.此时接收方就收到了2份同样的数据,这种情况会有特殊处理(如果不处理,就可能会发生一个支付请求,支付俩次这种情况),TCP存在一个"接受缓冲区"这样的存储空间(接收方操作系统内核里的一段内存),每个TCP,socket对象,都有一个接收缓存区(也有一个发送缓冲区),主机B收到主机A的数据,其实B的网卡已经读到数据了,把这个数据放到B对应的socket的接收缓冲区中(此处的接收缓冲区就相当于是一个阻塞队列),根据数据的序号,TCP很容易识别当前的俩条数据是否重复,如果重复就把后来的这份重复的数据丢了,这样就保证了read读到的数据,一定是不重复的
总结:由于接收缓冲区和序号的存在(接收的数据都是先放在接收缓冲区的,如果发生后发先至也没关系,由于序号的存在,我们很容易在接收缓冲区给数据重新排序和去重),即使数据重复了,顺序错乱了,接收方也能很好的处理.
重传的数据是否可能又丢包了?这是有可能的,因此超时重传可能会重传N次,当然N不会是一个很大的数字,当重传几次都丢包,此时再重传意义也不大了.(很可能是因为网络出现重大故障).因此当重传到一定次数后,此时就不会重传了,视为网络出现故障,接下来TCP会尝试重置连接(相当于断开连接,重连),如果还是失败,就彻底断开连接了,另外重传的时候,第一重传的时间间隔和第二次重传的时间间隔不一样,重传的轮次越大,间隔越大
TCP的可靠传输就是通过 确认应答 + 超时重传 来体现的,其中确认应答描述的是传输顺利的情况,超时重传描述的是传输出现问题的情况
连接管理
TCP建立连接
A和B建立连接,A中就有一部分存储空间存储了B的IP和端口,B中也有一部分空间存储了A的IP和端口,这俩部分信息被维护好了,此时A和B就建立了连接,而保存这部分信息的储存空间(数据结构)也称为 连接
TCP断开连接
A和B把自己储存的连接信息删了,此时就称为断开连接
那我们是如何具体的建立连接 和 断开连接的呢?
建立建立(三次握手)
通信双方需要各自记录对方的信息,彼此之间相互认同
SYN:客户端给服务器发起的建立连接请求称为SYN,也叫同步报文段
这里为什么要把中间俩次的响应和请求合并到一起?
这俩次交互的时机相同,都是操作系统内核在收到建立连接请求的时候,返回响应的同时也会给对方发送一个建立连接的请求 .另外,每次交互都需要封装和分用,交互成本很高,我把俩次交互合并成一次这样只用分装分用一次就可以了.
俩次握手能否完成建立连接的过程?
如果只有俩次,比如这里最后A没有给B返回一个ACK,在A的视角是可以的,知道了A保存至了B的信息,并且知道B保存了自己的信息,但在B的视角,B只知道自己保存了A的信息,但不知道A是否保存了自己的信息.另外三次握手还有个作用是,验证通信双方各自的发送能力和接收能力是否正常,如果是俩次握手,B是不知道自己的发送能力是否正常的
三次握手的意义:
1.让通信双方各自建立对对方的"认同"(保存了对方的信息)
2.验证通信双方各自的接收能力和发送能力
3.在握手的过程中,双方来协商一些重要参数
TCP在建立连接阶段的俩个状态
1.LISTEN
服务器的状态,表示服务器已经准备就绪,随时可以有客户端来建立连接
2.ESTABLISHED
客户端服务器都有的状态,表示连接建立完成,接下来可以正常通信了
TCP断开连接(四次挥手)
四次挥手和三次握手类似,都是通信双方向对方发起一个断开连接的请求,再各自给对方一个回应
这里四次挥手能不能变成三次挥手呢(中间的ACK和FIN能不能合并)
不能,FIN的发起不是由内核控制的,而是由应用程序调用socket的close方法(或者进程退出)才会触发FIN.ACK则是,当收到FIN,操作系统内核就会立即返回一个ACK,所以FIN和ACK发送的时机不同,这俩者之间会有一个时间差,服务器的程序执行到了对应的close方法才会触发FIN.当然如果执行程序执行close刚好和返回ACK的时机一样,此时也是可以合并的
TCP在断开连接阶段的俩个状态
1.CLOSE_WAIT
出现在被动断开连接的一方,表示等待关闭(等待调用close方法关闭socket)
2.TIME_WAIT
出现在主动发起断开连接的一方,假设是A是主动断开的一方,当A进入TIME_WAIT,相当于四次挥手已经挥完了.此时这里的TIME_WAIT要保持当前的TCP连接状态,不要立即释放.因为此时最后一个ACK刚刚发出去,还不确定有没有丢包,因此TIME_WAIT会等一段时间,之后如果没有收到重传的FIN,就认为最后一个ACK没有丢包,就彻底释放连接了(如果这里不保持连接状态,立即释放,如果最后一个ACK丢包了,连接断了,B是没法向A重传FIN的).TIME_WAIT会保持2MSL,意思是A如果经历了2MSL这个时间还没有收到重传的FIN,就认为这个ACK正常到达了(B没有重传FIN),MSL指的是,互联网上任意俩个节点数据传输消耗的最大时间,通常情况下这个值是60s