Lwip之TCP协议实现(一)

这篇具有很好参考价值的文章主要介绍了Lwip之TCP协议实现(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

TCP本身是一个相对复杂的协议,Lwip中最复杂的部分也是此处。这里,我们分部分描述。

第一部分:TCP处理

Tcp.c该文件提供了一些通用的函数接口。该文件中的函数主要的操作对象就是tcp_pcb,包括对tcp pcb的设置,修改读取等。另外,在该文件中还实现了tcp的定时器。

目录

一:Tcp的一些超时变量

二:Tcp的控制块(pcb)数据结构

1 计时变量

2 轮回时间评估变量

3 快速重传和快速恢复变量

4 拥塞避免与控制变量

5 发送序号、窗口相关变量

6 接收序号、窗口相关变量

7 Tcp段

三:定时器

3.1 Tcp的快速定时器:

3.2 Tcp的慢速定时器:

3.2.1 active状态pcb的超时处理

3.2.2 time-wait状态pcb的超时处理


一:Tcp的一些超时变量

#define TCP_TMR_INTERVAL       250 

#define TCP_FAST_INTERVAL      TCP_TMR_INTERVAL

#define TCP_SLOW_INTERVAL      (2*TCP_TMR_INTERVAL) 

TCP处理内部时间基准为250毫秒

快速定时器为250毫秒

慢速定时器为500毫秒
#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */

#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */

#define TCP_OOSEQ_TIMEOUT        6 /* x RTO */

#define TCP_MSL 60000  /* The maximum segment lifetime in microseconds */

MSL时间为60秒

FIN状态超时时间20秒

SYN_RECVD等待状态超时时间20秒

序号外数据超时时间6RTO
#define    TCP_NODELAY       0x01     /* don't delay send to coalesce packets */

如果支持no delay,那么所有发送的数据包将不被缓存。TCP协议对一些零散的小包采取合并发送,这样可以减少交互过程,提高带宽利用率,副作用是一些包的发送将被延迟。
#define TCP_KEEPALIVE  0x02    /* send KEEPALIVE probes when idle for pcb->keepalive miliseconds */


/* Keepalive values */

#define  TCP_KEEPDEFAULT   7200000     /* KEEPALIVE timer in miliseconds */

#define  TCP_KEEPINTVL  75000  /* Time between KEEPALIVE probes in miliseconds */

#define  TCP_KEEPCNT       9   /* Counter for KEEPALIVE probes */

#define  TCP_MAXIDLE  TCP_KEEPCNT * TCP_KEEPINTVL  /* Maximum KEEPALIVE probe time */

如果定义了keepalive选项,则要用到上面这些变量

Keepalive探测发送前等待的时间  7200秒 120分钟 2小时

发送keepalive探测的时间间隔 75秒 1分钟多

Keepalive探测发送的次数 9次

Keepalive总的可消耗时间 9*75秒 675秒 10分钟多

二:Tcp的控制块(pcb)数据结构

1 计时变量

tmr内部的计时变量,该变量包存了对应连接最后一次处于活跃状态的时间

polltmr poll定时计数

pollinterval Poll定时器间隔

rtime 重传计数

2 轮回时间评估变量

rttestrtseqsa以及sv等域被用来进行轮回时间评估。被用来进行轮回时间评估的段的序号被保存在rtseq中,该段被发送的时间保存在rttest中。平均轮回时间和轮回时间变量被保存在变量sasv中。在计算重传超时时间时这些变量就被使用,而这个基准的重传超时量保存在rto域中。Nrtx保存有重传的次数

3 快速重传和快速恢复变量

lastackdupacks两个域在实现快速重传与快速恢复的实现中使用。lastack域保存被接收到的最后一个ACK确认的序列号,dupacks指示有多少个这样的ACKs,这些已经被接收到的ACK序列号保存在lastack中。

4 拥塞避免与控制变量

当前连接的拥赛控制窗口被保存在cwnd域中,而慢启动的阈值保存在ssthresh中。

5 发送序号、窗口相关变量

Snd_nxt 保存有下一个要发送的序号

Snd_max 保存有最大发送序号

Snd_wnd 发送者窗口

Snd_wl1 保存有窗口最近更新段的序号

Snd_wl2 保存有窗口最近更新段的确认号

Snd_lbb 下一个将要被缓冲的数据字节序号

Acked 最新被确认的数据字节序号

Snd_buf 以字节为单位的可用发送缓冲空间

Snd_queuelen 以段为单位的可用发送缓冲空间

以上六个域ackdsnd_nxtsnd_wndsnd_wl1snd_wl2snd_lbb在发送数据时使用。被接收者确认的最高的序列号保存在ackd中,已发送的数据的最大序号保存在snd_max中,下一个将要发送的序列号由snd_nxt保存。接收者的建议窗口由snd_wnd保存,snd_wl1snd_wl2在更新snd_wnd时使用。snd_lbb含有传输队列中最后一个字节的序号。

6 接收序号、窗口相关变量

rcv_nxtrcv_wnd变量在接收数据时使用。rcv_nxt域含有下一个期望从远程终端得到的字节序号,因此在发送ACKs到远程主机时使用。接收者的窗口被rcv_wnd保持,在发送TCP段中会突出这一点。tmr域被用来作为一个定时器,在特定长度时间过去之后,当前TCP连接就应当被移除,比如像处在TIME-WAIT状态的连接。链路上所允许的最大段的大小被保存在mss域中,flags域保存有链路的额外的状态信息,像连接是否属于快速恢复,或者一个延迟的ACK是否应当被发送。

7 Tcp段

unsentunackedooseq三个队列在发送和接收数据时使用。从应用层接收到但是还没有发送的数据被加入unsent队列,已经发送但是还没有被远程主机确认的数据保存在unacked队列,接收到的序外数据缓冲在ooseq中。

后续考虑增加与窗口扩大选项有关的变量。根据定义,当扩大因子不为零时,即使为1,也需要大约128k的缓存空间。65535*21

三:定时器

协议中需要的定时器  总共有7个

连接建立定时器:syndrome发送后一定时间内没有收到响应,终止连接的建立

重传定时器:当长时间没有收到对端的acknowledge时会触发该定时器,认为数据包可能丢失,而进行重传。该定时器的时间是动态计算的

延迟acknowledge定时器:

坚持定时器:有发送方发出称为窗口探查的报文段,以避免在零窗口时产生死锁。

保活定时器:保活定时器用于在连接的双方或者其中的一方出现故障时,通过发送保活报文使客户或者服务器能够发现这种问题。此时连接可能长时间空闲,这时触发保活定时器的一个条件。

Fin-wait-2定时器:避免进入该状态后长时间收不到对端的fin。超时后关闭连接

Time-wait定时器:当连接的一方进入time-wait状态后启动该定时器,保证连接在time-wait时间内关闭

实现中,Tcp的定时器有两个:一个是快速定时器,一个是慢速定时器。这两个定时器被作为基准定时器用来实现更复杂的逻辑(大部分是通过定时计数的办法来实现)。

3.1 Tcp的快速定时器:

该定时器每隔250毫秒调用一次。它被用来发送延迟的ack

该函数遍历所有处于active状态的pcb,如果pcb的ack_delay标识被设置,一个空tcp确认段会被发送,之后,标识就被清除。

3.2 Tcp的慢速定时器:

该定时器没500毫秒调用一次。该定时器实现了tcp的超时重传定时器,以及用来移除处在time-wait状态足够长时间的pcb的定时器。它还被用来增加每个pcb中的类似于保活计数等一些计数变量

在该定时器中,会扫描未确认段组成的链(由tcp_seg结构体中的unacked指针指向),当未确认段所在的pcb的超时量大于设定的值时,超时重传就会发生。

对于处在TIME-WAIT状态中的连接,coarse grained timer也增加PCB结构体中的tmr域。当计数器达到2*MSL阀值后连接就被移除。

Coarse grained timer同样增加全局TCP时钟——tcp_ticks,该时钟在轮回时间评估和重传超时中使用。

该函数的具体逻辑如下:

{

   首先增加全局的tcp时钟—tcp_ticks。

   扫描所有的处于active状态的pcb,进行相应的处理。

   扫描遍历处于time-wait状态的pcb,进行相应的处理。

   //因为处在listen状态的pcb都是在等待对端的连接,不需要超时处理。

}

3.2.1 active状态pcb的超时处理

初始化pcb是否需要移除的变量 pcb_remove

如果当前pcb的状态是已发送syn(SYN_SENT),并且重传次数达到了syn最大的重传次数(TCP_SYNMAXRTX),则增加pcb_remove变量

否则,如果重传次数达到了tcp最大重传次数(TCP_MAXRTX),就增加pcb_remove变量。此时应该是某个ack的最大重传次数

如果上述两个条件都不满足:

pcb的重传定时器增加500个滴答。

如果该pcb的未确认段队列不为空,也就是说存在未确认的段,并且上述重传定时器计数值大于设定的重传超时时间,说明某个段很可能丢失了,超时重传将发生

{

    //此时,除非我们处于尝试连接某个对端,否则,将超时重传时间加倍。因为处于连接建立阶段的超时时间是固定的,典型值是75秒。

    如果当前不是在SYN_SENT状态,重新计算超时重传时间。(重传定时器的重传时间的取值是依赖与连接上测算到的RTT的)

    当超时发生时,表明发生了拥塞,tcp要执行慢启动和拥塞避免算法。(算法细节1234步骤卷一235页)。首先取拥塞窗口(cwnd)和接收者通告的窗口(snd_wnd)中的小值作为当前的窗口,将当前的阀值(ssthresh)减小到当前窗口的一半,如果阀值小于路径最大段,则设置为2个报文段的大小。(因为阀值是按照段的大小增加的,所以如果不小于一个的话就至少是两个)最后设置当前的拥塞窗口为一个段的大小。

    调用tcp_rexmit_rto()重传未确认队列上的第一个段。

}

//检查是否有pcbfin-wait-2状态停留了太长时间。

如果当前pcb是在上述状态,并且tcp定时器当前的滴答数tcp_ticks与该pcb中保存的变量tmr(该变量主要用于保存连接最后一次活跃时的时间)的差值大于设定的该状态的超时时间,则pcb_remove变量加一(该变量在后续移除不用的pcb时用到)。

//检查是否需要发送保活探测报文(KEEPALIVE)。

如果当前的连接支持保活探测选项(SO_KEEPALIVE),并且当前的连接处于建立(ESTABLISHED)或者CLOSE-WAIT状态

{

    如果当前时钟滴答数tcp_tickspcbtmr变量的差值大于保活探测前空闲的时间和保活探测报文发送消耗的最长时间之和,那么说明该连接空闲了太长时间,该连接不再需要。调用tcp_abort向对端发送一个RST报文,并释放与该连接相关的存储。

    否则,如果该时间在保活探测报文发送期间,还没有达到保活探测报文发送的最大次数,那么就调用tcp_keepalive继续发送保活探测报文,增加该pcb上的保活探测报文发送计数变量keep_cnt

}

//如果该连接上保存有序号外的数据,并且有很长时间不再活跃了,则将序号外的数据丢弃(这些数据如果最终需要传输的话还是会通过重传来完成的)

如果该连接的序号外段数据队列不为空,并且当前时钟滴答数和tmr的差值大于等于设定的TCP_OOSEQ_TIMEOUT,也就是说该连接空闲了这么长时间,则调用tcp_seq_free释放序号外段数据,同时队列也被设置为null

//检查确认当前连接是否在SYN-RECVD状态停留了太长时间

如果当前pcb的状态是SYN-RECVD,并且当前的空闲时间大于设定的TCP_SYN_RECVD_TIMEOUT,则增加pcb_remove变量,在后续处理中会将该pcb从链表上移除。

//检查确认当前连接是否在LAST-ACK状态停留了太长时间

如果当前pcb的状态是LAST_ACK,并且连接空闲时间大于2MSL,就增加变量pcb_remove。这有点类似与TIME-WAIT状态。说明即使是服务器,如果对端主动关闭了连接,但是服务应用没有关闭该连接,我们也不会停留很长时间,最长2MST后服务器端也就会关闭该连接。

//在这一部分的最后,我们根据pcb_remove变量的设置情况来对需要移除的pcb进行移除操作,并进行其他相关操作。

//虽然在之前我们将该变量多次进行自加,但是实际上该变量大于1的情况并不多,因为上述判断条件有好多是互斥的,所以在某个添加满足的情况下,其他许多都是跳过的。

如果该变量为真,说明需要移除该pcb。首先调用tcp_pcb_purge释放与该pcb相关的存储。将该pcbactive pcb链表上移除,释放其占用的内存,并将pcb指针指向下一个pcb块。

否则,说明没有需要移除的pcb。增加polltmr变量,如果poll时间大于设定的周期,则复位该变量。执行注册的poll函数,如果该函数正确执行了,则调用tcp_output看看该连接上是否有需要发送的数据,如果有的话就发送,即使是一个单纯的ack

pcb指针移到下一个pcb

如果新的pcb指针不为空,也就是说active链表上还有未处理的pcb,则继续回到active pcb处理的开始处进行下一次处理。

这样,我们处理了在active状态的pcb,并且涉及到tcp的SYN_RECVD  SYN_SENT  ESTABLISHED  CLOSE_WAIT  LAST_ACK  FIN_WAIT_2状态。从tcp的状态机来看,需要处理的状态就剩余CLOSING  TIME_WAIT  FIN_WAIT_1状态了。对于       CLOSING  FIN_WAIT_1状态不需要特殊处理。最后就剩余下面要处理的TIME_WAIT状态了。

3.2.2 time-wait状态pcb的超时处理

到这里就处理了active链表上的所有pcb,下面继续处理time_wait链表上的pcb。这部分的处理很简单,因为该链表上的所有pcb的状态都是time-wait的,所以只是进行2MSL超时的检查。

首先,将pcb_remove变量复位

如果当前连接空闲的时间大于2MSL,则将pcb_remove变量加一。

如果该变量被增加了,那么就释放与该pcb相关的存储,并将该pcb从链表上移除,释放其占用的内存。最后使其指向链表中的下一个pcb。

否则,说明该连接在time_wait状态还没有超时,不采取任何操作,只是将指针下移。

同样,如果新的pcb指针不为空,说明time_wait状态链表上还有未处理的pcb,接着返回的该处理的开始继续处理下一个time_wait状态的pcb。

接续:Lwip之TCP协议实现(二)_龙赤子的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-405721.html

到了这里,关于Lwip之TCP协议实现(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【lwip】13-TCP协议分析之源码篇

    TCP源码篇,当前只分析TCP层的源码实现,按功能分块分析,接口为RAW接口。 NETCONN接口和SOCKET接口会独立一篇文章进行分析。 本文基于读者已学习了TCP协议原理篇的基础上进行源码分析,不再在此篇文章中过多解析TCP相关概念。 ‍ 建议读者对着LWIP库源码进行阅读。对于初学

    2024年02月06日
    浏览(38)
  • 嵌入式TCP/IP协议栈-LwIP

    LWIP是一个轻量级的TCP/IP协议栈,其全称为Lightweight IP,它专门为小型嵌入式系统设计,具有占用资源少、易于移植、可裁剪性高等特点。 LWIP的设计目的是为嵌入式设备提供一个高效的TCP/IP协议栈,以便这些设备可以方便地访问Internet或局域网。它支持IPv4和IPv6协议,并实现了

    2024年02月09日
    浏览(52)
  • 1.物联网LWIP网络,TCP/IP协议簇

    一。TCP/IP协议簇 1.应用层:FTP,HTTP,Telent,DNS,RIP 2.传输层:TCP,UDP 3.网络层:IPV4,IPV6,OSPF,EIGRP 4.数据链路层:Ethernet,FrameRelay,Is-Is 注意:IWIP是完全按照TCP/IP协议来创建 2.OSI七层模型与TCP/IP协议模型 3.通讯过程 应用层:FTP协议 传输层:TCP协议 网络层:IP协议 链路层:

    2024年02月11日
    浏览(37)
  • 粉丝提问:设计和实现一个TCP协议半连接的端口扫描程序

    某学生粉丝发来问题: 这个题目一看就知道这位同学是网络安全相关专业。 很多粉丝以为彭老师知识搞驱动的, 但是其实作为一个拥有多篇网络协议专利的老鸟, 网络知识还是比较擅长的! 应用层套接字、组网、网卡驱动都有所涉猎, 目前还缺Linux内核协议栈这块没深入

    2023年04月14日
    浏览(42)
  • 【lwip】13-TCP协议分析之源码篇链接:[https://www.cnblogs.com/lizhuming/p/17438682.html](https://www.cnblogs.com/lizhuming/p/17438682.html)

    TCP源码篇,当前只分析TCP层的源码实现,按功能分块分析,接口为RAW接口。 NETCONN接口和SOCKET接口会独立一篇文章进行分析。 本文基于读者已学习了TCP协议原理篇的基础上进行源码分析,不再在此篇文章中过多解析TCP相关概念。 ‍ 建议读者对着LWIP库源码进行阅读。对于初学

    2024年02月06日
    浏览(80)
  • RT-Thread Studio配置LAN8720+LWIP+TCP服务器实现

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 由于项目上需要使用RT-Thread建立TCP服务器实现与客户端的数据交互,查阅了不少资料以及踩了不少坑,这里记录和分享一下实现的过程,希望能帮助到有需要的同学,您的支持是我创作的最大动力,谢谢

    2024年01月25日
    浏览(47)
  • 【通过STM32CubeMx配置LWIP+FreeRTOS并通过Socket实现TCP_Client/Sever】

    测试平台 → Nucleo_F429ZI 软件工具 → STM32CubeMx 6.6.1 MDK 5.3.0 网络调试助手V1.2 在CubeMx上面选择NUCLEO-F429ZI开发板,并采用默认配置 使能FreeRTOS 修改defaultTask的栈大小 由于使用了FreeRTOS,因此系统的TimeBase Source修改成TIM1 使能LWIP,并且先禁用DHCP,设置好静态IP Platform选择LAN8742 修改

    2024年02月08日
    浏览(46)
  • 关于STM32F4和GD32F4以太网,LAN8720+lwip+freemodbus,实现modbus tcp

    关于STM32F4和GD32F4以太网,LAN8720+lwip+freemodbus 这里使用了大佬 小灰灰搞电子 的代码,文章看 STM32F407+LAN8720移植Lwip和freeModbus实现MODBUS TCP 代码看 STM32F407+LAN8720+LWIP移植freemodbus TCP.zip 他的代码是基于正点原子F407的板子开发的,如果是别的板子,需要修改引脚 小灰灰的代码里,没

    2024年02月14日
    浏览(39)
  • 用Rust设计一个并发的Web服务:常用Rust库如Tokio、Hyper等,基于TCP/IP协议栈,实现了一个简单的并发Web服务器,并结合具体的代码讲解如何编写并发Web服务器的程序

    作者:禅与计算机程序设计艺术 1994年,互联网泡沫破裂,一批优秀的程序员、工程师纷纷加入到web开发领域。而其中的Rust语言却备受瞩目,它是一种现代系统编程语言,专注于安全和并发。因此,Rust在当下成为最流行的编程语言之一,很多框架也开始使用Rust重构,这使得

    2024年02月06日
    浏览(62)
  • LwIP 协议栈移植教程

            官网地址:lwIP - A Lightweight TCP/IP stack - Summary [Savannah]         按照官网的描述:lwIP is a small independent implementation of the TCP/IP protocol suite that has been initially developed by Adam Dunkels and is now continued here.(lwIP是TCP/IP协议套件的一个小型独立实现,最初由Adam Dunkels开发,现在在

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包