目录
TCP: 概述
TCP报文段结构
TCP往返延时(RTT)和超时
可靠数据传输
快速重传
流量控制
连接管理
TCP: 概述
- 点对点
- 一个发送方,一个接收方
- 可靠的、按顺序的字节流
- 不出错,不重复,不丢失,不失序
- 没有报文边界:发多个报文(MSS小)对方可能收到更多小报文或者几个大报文(MSS大)
- 管道化(流水线)
- 在未经确认的情况下,发送方可以给接收方发送连续很多TCP段(应用进程往下交的报文要被TCP根据MMS的大小分成多个TCP段,每个段加上头部信息)
- TCP拥塞控制和流量控制设置窗口大小
- 发送和接收缓存
- 发送端缓冲区为了检错重发,超时重传
- 接收端缓冲区为了平衡接收和发送速度
- 全双工数据
- 在同一连接中数据流双向流动
- MSS(Maximum Segment Size): 最大报文段大小(注意不包括头部)
- 面向连接
- 在数据交换之前,通过握手(交换控制报文)初始化发送方、接收方的状态变量
- 有流量控制
- 发送方不会淹没接收方
TCP报文段结构
- 这里的序号是字节为单位的序号,这里TCP段的的body的第一个字节占整个字节流的偏移量就是序号,就是前一节发送的时候用来标识的序号,因为每个段大小不一定一样所以序号不一定是连续的
- 确认号也是以字节为单位,TCP发送的确认号是累计确认的,但是要比当前已经接收的字节序号要大1。比如收到555ACK,那么554及以前的字节都收到了
- 首部长度:以四个字节为单位,说明首部有多长
- R,S,F用于两个应用进程建立TCP连接
- 没有固定接收方如何处理乱序的报文段,取决于实现者自己
TCP往返延时(RTT)和超时
- 怎样设置TCP超时
- 比RTT要长,但是RTT是变化的
- 太短:太早超时,造成不必要的重传
- 太长:对报文段丢失反应太慢,消极
- 怎样估计RTT
- SampleRTT:测量从报文段发出到收到确认的时间,如果有重传,忽略此次测量
- SampleRTT会变化,因此估计的RTT应该比较平滑,对几个最近的测量值求平均,而不是仅仅用当前的SampleRTT
- EstimateRTT = (1-α)*EstimateRTT + α*SampleRTT
- 定期的测量往返延迟
- 过去样本的当前RTT的影响呈指数型衰减
- 推荐值α=0.125
- 设置超时
- EstimateRTT+安全边界时间:EstimateRTT变化大(方差大)->较大的安全边界时间
- SampleRTT会偏离EstimateRTT多远
- DevRTT = (1-β)*DevRTT + β*|SampleRTT-EstimatedRTT|(注意,第一次计算时,DevRTT=0.5*SampleRTT)
- 当前采样值里偏差的程度的平均值(相当于方差/标准差)
- 推荐值β=0.25
- 超时时间间隔设置为
- TimeoutInterval = EstimatedRTT +4*DevRTT(“safety margin”)
可靠数据传输
- TCP在IP不可靠服务的基础上建立了rdt
- 管道化的报文段:GBN or SR
- 累计确认(像GBN,但是是传下一个期望发的序号ACK)
- 单个重传定时器(像GBN)
- 没有规范是否可以接受乱序的
- 通过一下事件触发重传
- 超时:只发那个最早的没有确认的段(像SR)
- 重复的确认: (比如收到了ACK50之后又收到3个ACK50)
快速重传:TCP为了防止万一,超时定时器设置的比较长,但是有时候发送接收很快,就在超时之前发送了下一个字节的ACK请求,如果说连着发了四个还没有超时,计时器就提前中断,开启下一次发送。
- 首先考虑简化的TCP发送方
- 忽略重复的确认
- 忽略流量控制和拥塞控制
- 产生TCP ACK的建议
接收方的事件 | 接收方动作 |
所期望序号的报文段按序到达 | 延迟的ACK。 对另一个按序报文段的到达 最多等待500ms。如果下一个报文段在 这个时间间隔没到达,发下一个ACK 因为发送方会连续发送多个文件,如果ACK 给太快,可能造成不必要的重发——忍住不发 |
有期望序号的报文段到达 另一个按序报文段等待发送ACK | 立即发送单个累计ACK,以确认两个按序报文段 |
比期望序号大的报文段乱序到达 检测出数据流中的间隔 | 立刻发送重复的ACK,指明下一个期待字节的序号 |
能部分或者完全填充接收数据间隔 的报文段到达 | 若该报文段起始于间隔(gap)的低端,则立刻发送ACK |
快速重传
流量控制
- 接收端缓冲区
- 取数据:用户
- 灌数据:IP网上灌到TCP然后TCP往缓冲区灌
- 通过rcv_window知道接受缓冲区还剩多少字节,发送ACK的时候会把容量发送给发送方(捎带技术)
- 实际上,两个实体互相发送数据的时候这个发送可以包含上次的ACK,这次的数据和缓冲区的容量,这样可以减小发送次数从而减小开销
- 目的:不让发送端发送太快使得接收端过载
连接管理
- 连接建立
- 连接建立的本质(三次握手)
- 知道要和对方通信(每一方都知道对方同意建立连接)
- 准备好资源,控制变量比如base置位然后发给对方,两方都必须知道(统一连接参数)
两端互相传数据的时候,约定好二者从x和y字节序号发送(二者都知道),不可以固定序号,可能造成老的连接对新的连接的干扰
- 两次握手的失败场景
- 有可能客户端发送了请求以后服务器的恢复超时了,然后客户端又发了一个请求,后面服务器两个连接请求都收到了,建立了两个连接但是实际上仅仅有一个连接是真正使用的,这样有一个连接是“半连接”(服务器维护连接而客户端没有维护)。注意每次连接都需要准备资源,这造成了资源浪费
- 也有可能使得老的数据被当成新的数据接收了
- 解决方案:三次握手
- 两次握手达咩!客户端问服务器活着么,服务器说我活着呢,但是万一客户端没等到服务器的回应就嗝屁了呢,你得让服务器知道对方是死是活
- 连接建立的本质(三次握手)
- TCP三次握手 (SYN=1就是连接请求)
- 左边发送初始序号,SYN报文请求建立连接
- 右边发送初始序号,SYN-ACK报文请求建立连接
- 左边确认,发送ACK报文(这里的ESTAB是Established)
- 解决半连接和接收老数据问题
- 第一种情况:客户端发现在上一次连接关闭之后,没有再向服务器发送建立连接的请求,因此拒绝
- 第二种情况:刚开始半连接就没有建立起来,服务器发现自己从来没有与客户端建立连接就收到数据了,因此拒绝
- 第三种情况(黑板图):客户端与服务器建立连接以后愉快的发送数据,结果其中一个数据突然不知道为啥滞留在中间,过了很长一段时间,客户端和,然后客户端和服务器的连接客户端仍然用相同的端口与服务器相同的端口建立连接,也不大可能把旧的数据当做新的数据接收,因为初始序号x和y不一样(要是一样的可能性太太小了),所以之前说x和y不是固定的数字可以防止新的连接被老的数据影响。x和y的选择和时钟周期有关系,把时钟周期的低32位当做初始序号。
- 关闭连接(它并不完美,有可能在中间会出问题)
- 连接是每个方向单独拆除的(两个来回)
- 客户端向服务器发送连接拆除请求,对方回应后说明客户端到服务器的连接已经拆除了
- 服务器向客户端发送链接拆除请求,对方回应后说明服务器到客户端的连接已经拆除了
- 过程
- 客户端,服务器分别关闭它自己这一侧的连接(发送FIN bit=1的TCP段)
- 一旦接收到FIN用ACK回应,接收到FIN段,ACK可以和它自己发出的FIN段一起发送
- 可以处理同时的FIN交换
- 连接是每个方向单独拆除的(两个来回)