Lwip 保活机制 2

文章转自老衲五木的新浪博客

这节讨论TCP的定时处理函数。在前面的讨论中,我们看到了与TCP的各种定时器,包括重传定时器、持续定时器和保活定时器,此外TCP中还有几个定时器我们还未涉及。这里总的来看看TCP中的各个定时器。TCP为每条连接总共建立了七个定时器,依次为:

1 )“连接建立(connection establishment)”定时器在发送SYN报文段建立一条新连接时启动。如果在75秒内没有收到响应,连接建立将中止。

2 )“重传(retransmission)”定时器在TCP发送某个数据段时设定。如果该定时器超时而对端的确认还未到达,TCP将重传该数据段。重传定时器的值 (即TCP等待对端确认的时间)是动态计算的,与RTT的估计值密切相关,且还取决于该报文段已被重传的次数。

3 )“延迟ACK(delayed ACK)”定时器在TCP收到必须被确认但无需马上发出确认的数据时设定。如果在200ms内,有数据要在该连接上发送,延迟的ACK响应就可随着数据一起发送回对端,称为捎带确认。如果200ms后,该确认未能被捎带出去,则定时器超时,此时需要发送一个立即确认。

4 )“持续 (persist)”定时器在连接对端通告接收窗口为0,阻止TCP继续发送数据时设定。由于连接对端发送的窗口通告不可靠(只有数据才会被确认,ACK不会被确认),允许TCP继续发送数据的后续窗口更新有可能丢失。因此,如果TCP有数据要发送,但对端通告接收窗口为0,则持续定时器启动,超时后向对端发送 1字节的数据,判定对端接收窗口是否已打开。

5 )“保活(keep alive)”定时器在TCP控制块的so_options 字段设置了SOF_KEEPALIVE选项时生效。如果连接的连续空闲时间超过2小时,则保活定时器超时,此时应向对端发送连接探测报文段,强迫对端响应。如果收到了期待的响应, TCP可确定对端主机工作正常,在该连接再次空闲超过 2小时之前,TCP不会再进行保活测试。如果收到的是RST复位响应, TCP可确定对端主机已重启。如果连续若干次保活测试都未收到响应, TCP就假定对端主机已崩溃,但它无法区分是主机故障还是连接故障。

6) FIN_WAIT_2定时器,当某个连接从FIN_WAIT_1状态变迁到FIN_WAIT_2状态并且不能再接收任何新数据时,FIN_WAIT_2定时器启动,设为10分钟。定时器超时后,重新设为75秒,第二次超时后连接被关闭。加入这个定时器的目的是为了避免如果对端一直不发送 FIN,某个连接会永远滞留在FIN _ WAIT_ 2状态(假设TCP不选用半打开功能)。

7) TIME_WAIT定时器,一般也称为2MSL定时器。2MSL指两倍的MSL,即最大报文段生存时间。当连接转移到TIME_WAIT状态,即连接主动关闭时,定时器启动。状态转换图那一节中已经详细说明了需要2MSL等待状态的原因。连接进入TIME_WAIT状态时,定时器设定为1分钟,超时后,TCP控制块被删除,端口号可重新使用。

前面的7个定时器中,重传定时器使用rtime字段计数,持续定时器使用persist_cnt字段计数,其他五个定时器除延迟ACK定时器外都使用rtime字段计数,从上面的描述中可以看出,这四个定时器是TCP处于四种不同的状态时使用的,因此四个定时器完全独立的使用rtime字段而不会互相影响。延迟ACK定时器使用系统250ms周期性定时来完成的。

LWIP中包括两个定时器函数:一个函数每 250 ms调用一次(快速定时器);另一个函数每500ms调用一次(慢速定时器)。延迟ACK定时器与其他6个定时器有所不同:如果某个连接上设定了延迟ACK定时器,那么下一次250ms定时器超时后,延迟的ACK必须被发送(实际的ACK延迟时间在0~250ms之间)。其他的6个定时器每500 ms增加1,当计数值超过某些阈值时,则相应的动作被触发。

先看简单的快速定时器处理函数:

void tcp_fasttmr(void)

{

struct tcp_pcb *pcb;

for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { //遍历整个active链表

if (pcb->refused_data != NULL) { // 如果某个控制块还没有数据未接收

err_t err;

TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); //调用上层函数接收数据

if (err == ERR_OK) {

pcb->refused_data = NULL; // 成功接收则复位指针

}

}

if (pcb->flags & TF_ACK_DELAY) {//若控制块开启了延迟ACK定时器

tcp_ack_now(pcb);    // 发送一个立即确认

pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); //清除标志位

}

}// if

}// for

从上面可以看出,快速定时器处理函数主要做了两方面的工作,一是向上层递交上层一直未接收的数据,二是发送该连接上的立即确认数据段。与快速定时器相比,慢速定时器处理函数就显得相当的庞大了,这里我们只列出和各个定时器相关的部分,而对其他部分采用伪代码的方式加以描述,当然,这里所谓的其他部分就是我们在前面已经讲解过的部分了。

void tcp_slowtmr(void)

{

++tcp_ticks;

pcb = tcp_active_pcbs;

while (pcb != NULL) {

pcb_remove = 0;

该控制块的零窗口探查处理;

该控制块的超时重传处理;

if (pcb->state == FIN_WAIT_2) {  // FIN_WAIT_2定时器超时

if ((u32_t)(tcp_ticks - pcb->tmr) >

TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {

++pcb_remove;

}

}

该控制块的超时保活处理;

#if TCP_QUEUE_OOSEQ

if (pcb->ooseq != NULL &&  // 丢弃在ooseq队列中长时间未被处理的数据

(u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {

tcp_segs_free(pcb->ooseq);

pcb->ooseq = NULL;

}

#endif

if (pcb->state == SYN_RCVD) { // SYN_RCVD状态超时

if ((u32_t)(tcp_ticks - pcb->tmr) >

TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {

++pcb_remove;

}

}

if (pcb->state == LAST_ACK) { // LAST_ACK定时器超时

if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {

++pcb_remove;

}

}

若有超时则删除控制块;

无超时则周期性的外发数据包(poll);

取得active链表上的下一个控制块;

}// while

pcb = tcp_tw_pcbs;  //处理TIME-WAIT链表

while (pcb != NULL) {

pcb_remove = 0;   // TIME-WAIT定时器超时

if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {

++pcb_remove;

}

若超时则删除控制块;

取得TIME-WAIT链表上的下一个控制块;

}// while

}//函数尾

可以看出各个定时器的实现都是利用全局变量tcp_ticks与tmr字段的差值来实现的。当TCP进入某个状态时,就会将相应tmr字段设置为当前的全局时钟tcp_ticks的值,所以上面的差值可以有效表示出TCP处于某个状态的时间。各个定时器超时后的处理也很相似,即将变量pcb_remove加1,pcb_remove变量是超时处理中最核心的变量了,当针对某个PCB控制块作完超时判断之后,函数通过判断pcb_remove的值来处理TCP控制块,当pcb_remove值大于1时,则表示该控制块上有超时事件发生,该控制块或被删除或被挂起。注意伪代码中的重传定时器超时并不会影响pcb_remove的值。如果细心,你还可以看到,上面的代码多了两个超时事件,即SYN_RCVD状态超时和ooseq队列数据超时,当然,这两个超时事件并不影响协议栈功能的实现。

最后来看看系统为每个超时时间设置的超时时间,从上面的代码中可以看出,它们是在各个宏定义里面实现的。

#define TCP_TMR_INTERVAL       250   //250ms

#define TCP_FAST_INTERVAL      TCP_TMR_INTERVAL  // 快速定时器

#define TCP_SLOW_INTERVAL      (2*TCP_TMR_INTERVAL) // 慢速定时器

 

#define TCP_FIN_WAIT_TIMEOUT   20000  // FIN_WAIT_2状态超时时间

#define TCP_SYN_RCVD_TIMEOUT  20000  // SYN_RCVD状态超时时间

 

#define TCP_OOSEQ_TIMEOUT   6U   //ooseq队列中数据等待的rto周期数

#define TCP_MSL  60000U   // MSL

到这里,我们的PCB控制块中的各个字段基本都已涉及到了,除了polltmr和pollinterval。这两个字段用于周期性调用函数tcp_output,用以发送控制块上残留的未发送数据段。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
lwip tcp保活是一种机制,用于检测TCP连接是否仍然有效并保持连接的稳定性。在lwIP协议栈中,通过设置SO_KEEPALIVE标志来开启TCP保活选项。当连接处于ESTABLISHED或CLOSE_WAIT状态时,如果开启了保活选项,lwIP会定期发送TCP保活探测数据包来检测对方是否仍然存在。 具体实现中,lwIP使用了两个参数:keep_idle和keep_intvl。keep_idle表示首个保活探测包发送之前的空闲时间,keep_intvl表示连续保活探测包之间的时间间隔。 如果在pcb->keep_cnt * pcb->keep_intvl的时间内没有收到对方的数据,则lwIP认为对方已经断开连接或不可达,会直接关闭本地连接。在代码中,如果满足这个条件,会输出相应的信息并关闭本地连接。 综上所述,lwip tcp保活是通过定期发送保活探测包来检测对方是否仍然存在,并在超时后关闭本地连接。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [LWIP使用TCP自带的KEEPALIVE功能](https://blog.csdn.net/qq446252221/article/details/110439520)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [CUBE配置STM32H750+Lan8720+FreeRTOS+lwip+掉线重连+KeepAlive移植工程文件 ](https://download.csdn.net/download/monei3525/12913098)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [TCP/IP传输层协议实现 - TCP保活定时器(lwip)](https://blog.csdn.net/arm7star/article/details/117382423)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值