tcp的一些感想
在tcp协议中, 我们考虑下面几个问题:
- 消息可以发送
- 消息能解析出正确的发送数据
- 消息确认到达
- 尽量的压榨性能
按照惯例, 先放总结
总结
ack机制
这个类似的有kafka的消息确认机制, rabbitmq的确认机制.
变种的有心跳监测机制
本地buffer批量发送
kafka本地先buffer然后批量发送
mysql设置buffer区域, 先写入buffer然后批量刷盘, 刷盘的触发机制有buffer满或者达到超时时间.
异常处理
- tcp设置urg=1告诉server这条消息优先级比较高, 先处理.
数据校验
- 将数据和校验位一起发送, 然后server确认数据是否正确, 类似于https的连接
序列号
- 用有序数组来确认顺序还是添加序号确认顺序, 这是个问题.
随机序列号
- 初始给出一个随机序列号防止被黑, 跟https中建立连接的过程有点关系
三次握手和四次挥手
三次握手
在tcp中client和server是平等的, 都接收和发送消息, 所以必须要确认都能收都能接
1 | c: 我能发, 你能收么, 你看我打算从x这个初始序列号给你发消息 |
感觉这里之所以x+1是为了编码实现的方便, 跟虚拟内存一样.
四次挥手
1 | c: 我不打算发了, 我发的最后一个是$u$ |
差错控制机制
分段
由于受到网络最大传输单元的限制, tcp不可能一次就将所有的消息发送完成, 而需要将消息分段发出, tcp发送数据的基本单位是段.
tcp每段数据开头都带有一个标示该段数据的32位序号, 这里类似于数组的地址就是数组开头第一个元素的地址一样
确认应答
确认应答采用滑动窗口的方式
发送端的发送窗口大小 = min[拥塞窗口, 接收端公告的接收窗口]
client在发送完窗口大小的数据后, 等待接收端的确认消息.
确认过程
- server收到消息后可以逐个确认也可以批量确认, 然后给出确认序号是最后一个发送序号+1, 也就是告诉client下次接收消息的开始序号
- 一般来说, 确认消息会捎带在server发送的数据中, 这样可以提高网络传输的效率.
- ack返回有两个条件, 一个是跟着server的数据一起返回, 另一个是超时, 这个超时时间不能大于client重传定时器的超时时间
- todo
- 这里类似于本地一个buffer, 然后buffer满了就发送, 或者等待超时就发送
- kafka先将消息buffer到本地,然后一起发送,这样可以提高效率.// 需不需要考虑本地消息丢失的问题
错序确认
- 如果因为网络抖动或者什么原因, 4001消息的确认在2001的前面, 那么server会理科向client发送确认应答, ack的value是正确的确认序号+1, 也就是2001. 这样就会产生很多2001的ack, client根据这个ack确认是否需要重发. server会将这些错序的消息buffer到本地, 直到收到正确的
消息丢失
- 消息丢失的话, 后面的消息会一直重复确认, 逐渐将重传定时器溢出, 然后重发
所以乱序和丢失的区别在于先是乱序的那个序号到达还是client的重传定时器溢出????
重传机制
- client发送一个tcp报文, 会将这个报文暂存到缓冲区, 并设置一个buffer超时时间, 过了这个时间如果没有收到ack的话就会重传.
- 问题的关键在于如何设置这个超时时间
- client收到了server的多个重复的ack, 默认是3个, 那么就会重新发送消息
- 重传的话到底发送哪条消息?
- 2的话只发送丢的那条就ok
- 1是因为超时或者网络不稳定什么的, 发送丢的那条和后面的全部消息
tcp的拥塞机制
慢启动
重传定时器溢出的时候向下调整
连续接收到多个重复确认应答的时候向下调整