Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LWIP
lwip的tcp超时重传实现原理
发布于 2021-08-10 15:04:50 浏览:3389
订阅该版
[tocm] # 一、概述 tcp是一种面向连接的可靠的运输层协议,在TCP/IP协议族里,同属于该层的协议是UDP,如下图的TCP/IP分层模型所示。(该图来自TCP-IP详解卷1:协议)。之所以能够为应用层提供可靠的传输,是因为协议中采用了多种策略:超时重传,拥塞避免,保活机制,糊涂窗口等。而本篇文章重点是分析其中的超时重传的原理。 ![image.png](https://oss-club.rt-thread.org/uploads/20210810/8c89b1eee4ad08e420f032c053ca68dd.png.webp) # 二、tcp超时重传 tcp为了保证两端的主机能够传输正确的数据,采用了超时重传和正面确认的策略,tcp数据交互如下图)(节选自https://zhuanlan.zhihu.com/p/142665708),该图描述了一次数据传输经历的建立连接、数据交互、断开连接的过程。 ![image.png](https://oss-club.rt-thread.org/uploads/20210810/d786b7a492f41d0b689d3014e557f92b.png.webp) tcp传输是全双工的,客户端和服务端独立的进行传输,所以各有一套独立的序列号,每次发送报文段后都会启动一个超时传输定时器,如果在超时后没有收到确认报文段,则会进行重发,错误可能发生在发送数据上,也可能发生在确认报文段上。如下图(选自https://www.cnblogs.com/-wenli/p/13080675.html)。在出现如下图中的两种数据发送失败的情况下,TCP会重传该报文段。 ![image.png](https://oss-club.rt-thread.org/uploads/20210810/44dc6144e41fedb0424683fbaaa3ab43.png.webp) 本章小结:tcp在发起一次传输时会开启一个定时器,如果在定时器超时时未收到应答就会进行重传。 # 三、往返时间测量和估计 上一章讲述了TCP超时重传的原理,这一章介绍超时时间估计RTO怎么确定,如下图描述了RTO选大或选小的弊端(该图选自https://www.cnblogs.com/-wenli/p/13080675.html),如果RTO较大,由于等待时间过长会影响TCP的效率;较小则可能使系统来不及响应而认为数据传输失败而重传。所以RTO是随着网络的状态动态调整的,而网络状态可以通过测量报文段的往返时间来反应。 ![image.png](https://oss-club.rt-thread.org/uploads/20210810/5636b97f9830406b9ab6c2db7bac12bd.png.webp) jacobson的RTO估计算法: ``` Err = M-A A = A+g*Err D = D+h*(|Err|-D) RTO = A+4*D M:某次测量的RTT值 A:RTT平均值 D:RTT方差 g:常数,一般取1/8 h:常数,一般取1/4 ``` 从jacobson的RTT平滑算法可以知道,RTO估计是基于RTT的均值和方差来计算的,是一种可以平滑较大变化的计算方法。 # 四、lwip源码分析 ## 4.1 RTO估计 ```c /* RTT估计计算。这是通过传入的确认报文段来得到的报文段的往返时间。*/ if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { /* diff between this shouldn't exceed 32K since this are tcp timer ticks and a round-trip shouldn't be that long... */ m = (s16_t)(tcp_ticks - pcb->rttest); LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", m, (u16_t)(m * TCP_SLOW_INTERVAL))); /* This is taken directly from VJs original code in his paper */ m = (s16_t)(m - (pcb->sa >> 3)); pcb->sa = (s16_t)(pcb->sa + m); if (m < 0) { m = (s16_t) - m; } m = (s16_t)(m - (pcb->sv >> 2)); pcb->sv = (s16_t)(pcb->sv + m); pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv); LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL))); pcb->rttest = 0; } ``` 在输出一个报文段使,将全局变量时钟滴答tcp_ticks赋值给tcp控制块的rttest字段表示RTT测量开始的时间,如下图中选自tcp_out.c的tcp_output_segment的源码,源码在估算RTO时首先用当前的tcp_ticks - pcb->rttest表示RTT的测量值m,然后基于jacobson算法计算rto的值。 ![image.png](https://oss-club.rt-thread.org/uploads/20210810/1cf6194f4fdb3beaa0c8c4d2201730a0.png.webp) ## 4.2 超时重传 超时重传是在tcp_slowtmr()函数里完成的,内核会每500ms执行一次该函数,在函数中判断pcb->rtime是否大于pcb->rto来判断是否超时,如果超时则执行tcp_rexmit_rto_commit()函数来重传报文段。 ```c if (pcb->rtime >= pcb->rto) { /* Time for a retransmission. */ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F " pcb->rto %"S16_F"\n", pcb->rtime, pcb->rto)); /* If prepare phase fails but we have unsent data but no unacked data, still execute the backoff calculations below, as this means we somehow failed to send segment. */ if ((tcp_rexmit_rto_prepare(pcb) == ERR_OK) || ((pcb->unacked == NULL) && (pcb->unsent != NULL))) { /* Double retransmission time-out unless we are trying to * connect to somebody (i.e., we are in SYN_SENT). */ if (pcb->state != SYN_SENT) { u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff) - 1); int calc_rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx]; pcb->rto = (s16_t)LWIP_MIN(calc_rto, 0x7FFF); } /* Reset the retransmission timer. */ pcb->rtime = 0; /* Reduce congestion window and ssthresh. */ eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); pcb->ssthresh = eff_wnd >> 1; if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) { pcb->ssthresh = (tcpwnd_size_t)(pcb->mss << 1); } pcb->cwnd = pcb->mss; LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F " ssthresh %"TCPWNDSIZE_F"\n", pcb->cwnd, pcb->ssthresh)); pcb->bytes_acked = 0; /* The following needs to be called AFTER cwnd is set to one mss - STJ */ tcp_rexmit_rto_commit(pcb); } } ```
5
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
happycode999
这家伙很懒,什么也没写!
文章
28
回答
6
被采纳
0
关注TA
发私信
相关文章
1
RT-THREAD在STM32H747平台上移植lwip
2
{lwip}使能RT_LWIP_DHCP时可以获取到ip
3
stm32f103 LWIP 2.0.2 TCP收发问题
4
lwip2.1不重启修改IP
5
关于网络协议栈的测试
6
可否将LWIP升级到2.1.2 和 2.0.3?
7
socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
8
tcpclient 插拔网线问题?
9
两个tcpclient同时通讯可以吗?
10
SO_BINDTODEVICE 未定义该如何解决
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
1
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部