TCP协议:传输控制协议
1、协议实现
- 16位源端端口&16位对端端口:描述通信俩端进程
- 32位序号:告诉接收端,这条数据在整体数据中的排序,接收端根据序号进行排序
- 32位确认序号:向发送端进行回复确定,确认序号之前的数据都已经收到
- 4位头部长度:以4字节为单位描述tcp报文头部长度,tcp报头最长是60字节,最小20字节。
- 6位保留:暂时未使用
- 6位标志位:FIN,SYN,RST,PUSH,ACK,URG
- 16位窗口大小:用于实现滑动窗口机制,进行流量控制
- 16位校验和:二进制反码求和算法,校验数据一致性
- 16位紧急指针:指向带外优先数据的结束位置
- 0~40字节选项数据:通常是一些额外的协商信息
2、tcp协议特性
面向连接,可靠传输,面向字节流传输服务
2.1面向连接
通信前,要先建立连接,确保双方都是在线,具有数据收发的能力。
连接管理:三次握手建立连接,四次挥手断开连接。
2.1.1三次握手
通信前,要先建立连接,确保双方都是在线,具有数据收发的能力。
2.1.2四次挥手
通信结束后,会有一个断开连接的过程,避免出现意外。
FIN请求的功能: 只能表示不再给对方发送数据,不代表不接受数据
CLOSE_WAIT:等待关闭,对方发送了FIN包,已经不再给自己发送数据,上层如果这时候还在继续recv,则会读完缓冲区数据后,不再阻塞,而是返回0.这种情况下就是等待上层针对这种情况的处理。
2.1.3问题
2.1.3.1为什么握手是三次?
握手三次:俩次不安全,四次没必要
1.通信前,要先建立连接,确保双方都是在线,具有数据收发的能力。因此都要SYN
2.俩次不安全:
- 有可能SYN会延迟到达,与重发的SYN形成冲突(三次有状态要求)
- 防止恶意攻击,比如客户端发送SYN后就直接退出
3.四次没必要:没必要发送俩次报文,在一次回复中将对应比特位置置1即可
2.1.3.2 挥手是四次?
FIN请求只能表示主动关闭方不再发送数据,不代表不再接收数据。因此,被动关闭方收到FIN包并进行确认后,还有可能会继续发送数据。等待上层不再发送数据了,也要关闭套接字了才会发送FIN包。
因此挥手没有合并为三次。
2.1.3.3 三次握手失败,俩端如何处理?
- 客户端发送第一次握手请求失败了,客户端会重传请求SYN。
- 第二次握手失败服务端回复的ACK + SYN丢失,客户端会重传,服务端在等待对方ACK回复超时后,给客户端发送一个RST报文,然后释放资源。
- 第三次握手失败,服务器超时,回复RST,然后释放资源。
2.1.3.4 一台主机出现了大量的CLOSE_WAIT状态连接,是什么原因?
- 只有收到FIN请求并进行了确认回复的连接会进入CLOSE_WAIT状态
- 一直处于CLOSE_WAIT而没有进入下一个状态,是因为上层没有进行关闭套接字操作,也就是没有发送FIN,所以没有进入下一步
- 因此原因就是代码中没有针对断开连接的套接字进行关闭处理
2.1.3.5 TIME_WAIT状态有什么用,为什么不直接关闭套接字释放资源?
- TIME_WAIT状态是主动关闭方在发送最后一次ACK后进入的状态
- 如果没有TIME_WAIT,主动关闭方直接释放套接字资源,有可能出现新启动的套接字使用了与之前相同的地址信息
- 而上次通信可能最后一次ACK丢失,一旦丢失被动关闭方会重传FIN
- 就会导致上一次通信因为最后一次ACK丢失,而遗留问题(重传FIN)对新连接造成影响
因此不能直接释放资源,需要等待俩个MSL时间,针对有可能存在的FIN重传进行处理,并保证上一次通信的所有数据都消失在网络中。
MSL:报文最大生命周期,一个报文在网络中最大能存在的时间。默认60s
2.1.3.6 一台主机上出现了大量TIME_WAIT状态连接,是什么原因?怎么处理?
- TIME_WAIT是状态是主动关闭方在发送最后一次ACK后进入的状态,等待一段时间是为了处理有可能因为FIN丢失导致的FIN重传的处理
- 因此一台主机出现大量的TIME_WAIT连接,是因为主机上大量的主动关闭了连接,常见于爬虫服务器
- TIME_WAIT等待时间是可以配置的,可以将时间缩短
- 有个套接字选项,叫做地址重用:setsockopt();
2.1.3.7 tcp连接管理中的保活机制
连接断开有个信息:recv会返回0,send会触发异常SIGPIPE
tcp通信中,如果客户端和服务端的通信频率并不高,中间突然断网了,没有四次挥手的机会,如果俩端通信频率很低,可能需要很久才能发现。
在通信中,客户端与服务器若长时间无通信(默认7200s),则tcp服务器会自动向客户端发送保活探测心跳包,要求对方进行响应(默认每隔75s),若连续多次都没有收到响应(默认9次),则认为断开连接。
这些数据都可配置。甚至可以用套接字选项设置。
通常网络通信程序,在初始化阶段都会自定义SIGPIPE信号。
2.2可靠传输(通过很多特殊机制实现)
- 面向连接——确保双方都具有数据收发能力
- 丢包检测机制(确认应答机制)——接收方要针对收到的每一条数据进行确认回复
- 丢包重传机制(超时重传机制)——等待确认回复超时则重传
- 序号字段——进行包序管理,有序交付
- 校验和字段——校验数据一致性,不一致则丢弃,要求重传
tcp为了实现可靠传输使用了很多机制,但是这些机制影响了tcp的传输性能,但是有些性能损失是可以挽回的:
- 比如确认应答丢失所导致的重传就没有必要
- 比如发送方数据过多,超过了接收方的上限,导致数据溢出丢包
- 比如因为网络状态拥塞时发送数据过多导致丢包
2.2.1 滑动窗口机制:流量控制
2.2.1.1 作用
通过协议字段中的窗口大小字段,接收方告诉发送方最大的发送数据量,避免因为发送数据过多,而自己缓冲区不够,导致溢出丢包。窗口大小字段中的值不能大于接收方接收缓冲区中剩余空间的大小。
2.2.1.2 实现
MSS:最大数据段大小,表示在tcp传输中,应用层数据最大大小
在三次握手阶段,tcp通信双方就会进行协商。
MTU:最大传输单元大小,是链路层限制的最大数据帧大小。
TCP在使用send发送数据的时候,是把数据放到发送缓冲区中,系统选择合适的时候,从发送缓冲区中取出合适长度大小(不大于MSS)的数据,封装报头进行发送。
2.2.1.3 特殊协议
- 停等协议:发送一条数据后,必须等到确认回复才会发送下一条
- 回退n步协议(针对网络一般):发送一条数据后,若丢包,则将丢包的数据及其往后的数据都进行重传
- 选择重传协议(针对网络好):管线化传输,哪条丢了就重传哪条
2.2.2 快速重传协议
在连续传输过程中,接收方会按序响应。如果某个确认应答丢了,也没事,因为后面的数据确认应答也能表示前面的数据已经收到了,(基于确认序号的作用)避免因为确认应答丢失而导致的重传。
在连续传输过程中,如果某个数据包丢了。
就是说在传输过程中,接收窗口起始序号位置的数据没有收到,而是收到了后面的数据,这时候就可以认为前边数据有可能丢失。这时就可启动快速重传协议。
收到了后面的数据,但是没有收到前面的数据,则每收到一条数据就会确认回复一下前面没有收到的那条数据的序号,表示让对方对这条数据进行重传。
但是发送方,收到了前边的数据的确认序号应答,不会立即对这条数据进行重传,而是连续收到三次后才会重传。避免前面数据这是延迟到达而不是丢了的情况。
2.2.3 拥塞机制
避免因为网络拥塞而导致大量丢包。实际上就是一种网络探测式的传输。
1拥塞窗口以指数级进行增长:1,2,4,8,16.......
2增长过程中,一旦出现了超时重传请求:
- 阈值变为当前窗口的一半大小,窗口从1重置
- 当拥塞窗口达到阈值大小时,变为线性增长,每次+1
3增长过程中,一旦出现了快速重传请求
- 拥塞窗口变为当前窗口的一半
- 阈值变为拥塞窗口+3
拥塞控制就是慢启动,块增长的形式实现网络状况探测,避免因为网络拥塞而导致是大量丢包。
3、其他提升性能的方式
- 延迟发送机制:数据先放到缓冲区,延迟一段时间再发送(减少IO次数)
- 延迟应答机制:收到数据后,延迟一段时间再确认回复(维持吞吐量)
- 捎带应答机制:将确认回复与即将要发送的数据一起合并发送
- 快速重传协议:尽量减少超时等待
4、面向字节流
提供的是字节流传输服务
流式传输:是一种有序的,安全的,可靠的,基于连接的字节流传输
字节流传输:发送或接收最小单位为字节的传输方式。
过程:方式方将要发送的数据放到发送缓冲区中,系统会截取合适大小的数据进行传输。接收方将接收到的数据,放到接收缓冲区中,上层recv要多少就多少。
字节流传输是比较灵活的,没有发送或者接收的长度限制。
4.1问题:粘包问题
4.1.1定义
将多条数据当作一条数据进行处理(俩个包粘在一起当作一个包了)
4.1.2本质原因
tcp对上层数据的边界不做特殊处理,没有数据之间的边界管理
4.1.3解决方法
1.数据之间以特殊字符作为间隔
优点:思想简单
缺点:数据中不能有特殊字符,需要编码
2.数据定长
优点:思想简单
缺点:数据小了浪费性能,数据大了不够
3.采用TLV数据格式:type,length,value
发送每条数据都有一个固定的头部:type,length