加载中…
个人资料
老衲五木
老衲五木
  • 博客等级:
  • 博客积分:0
  • 博客访问:353,912
  • 关注人气:330
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
博文
标签:

lwip

老衲五木

嵌入式网络那些事

tcp连接异常

分类: LwIP常见问题
    LwIP所有版本包括最新的2.0版本具有以下缺陷,当用户使用raw编程并在err或poll回调函数中操作了内核全局tcp_active_pcbs链表(最典型的,比如进行了重连操作),将有可能导致链表异常,严重情况下,链表中的很多tcp_pcb会丢失,从而导致部分连接没有任何反应,出现假死的现象。
    具体信息为(以1.4.1为例),在tcp.c文件的tcp_slowtmr函数中,截取如下部分。
    请参考下面的蓝色步骤(1)->(2)->(3)->(4)->(5)->(6),该流程路径会引发问题。红色部分为需要添加的代码以修正这个BUG。
tcp_slowtmr_start:
  prev = NULL;
  pcb = tcp_active_pcbs;
  if (pcb == NULL) {
    LWIP_DEBUGF(TCP_DEBUG, ('tcp_slowtmr: no active pcbs\n'));
  }
  while (pcb != NULL) {
    if (pcb->last_timer == tcp_timer_ctr) {//(4)已经处理过的连接不再被处理
标签:

lwip

raw

tcp_tmr

sequentialapi

杂谈

分类: LwIP常见问题

网友来信:

五木您好! 
     感谢您在百忙之中抽空看我的邮件,抱歉今天又打扰您了,《嵌入式网络那些事:LwIP协议深度剖析与实战演练》已经买到了,在京东上买的,打8.5折,我也给我同事推荐了,大家最近被LwIP搞得有点晕,哈哈。
     我还有一些疑问想请教您一下:
    1、LwIP为我们提供了两种应用程序接口来实现TCP/IP协议栈,分别是RAW API和sequential API,我用的ARM芯片是STM32F103ZET6,ROM是512k,RAM是64K,跑的是uC/OS-II操作系统,您觉得用哪种应用程序接口好一点?
    2、我现在用的是RAW API应用程序接口实现TCP/IP协议栈,现在的情况是这样:我自己建了一个任务,这个任务用于与服务器建立通讯实现数据交换。我现在想在这个任务里建个应用程序框架,这个框架主要用于建立网络连接、接收服务器数据、发送数据给服务器,内核状态(pcb->state)查询、内核错误标志的处理和关闭网络连接等。我现在就是想:1、用个什么样的方法能够得到内核的工作状态,比如现在服务器发了关闭连接信号FIN,客户端此时会进入CLOSE_WAIT状态,那么在不改动内

标签:

lwip

tcp

接收窗口为0

raw

api

分类: LwIP常见问题

前两天网友来信,又是同样的一个问题,这也是好多基于raw api做开发的朋友最容易忽略的一个问题,来信原文摘录(陈兄,侵犯您版权啦。。。):

五木您好!
很荣幸在网上看到您发表的一些关于lwip的文章,写的非常好。
我现在有个小问题想请教您一下,就是服务器发命令给我,然后我回复服务器,在回复的TCP首部中,窗口一直在减少,最初是2920个字节,最后减成0了,然后服务器就不跟我通讯了。后来我把下面红色语句部分改成pcb->rcv_ann_wnd = pcb->rcv_wnd,使得rcv_ann_wnd是一个定值,但这样rcv_ann_right_edge 会在不停地变大,这样要不要紧呀,麻烦您指点我一下,非常感谢。
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
{
u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {

pcb->rcv_ann_wnd = pcb->rcv_wnd;
return new_right_edge - pcb->rcv_ann_right_edge;
}
标签:

it

lwip

tcp

api

分类: 嵌入式网络那些事

    感谢广大机油吊丝们对《LwIP协议栈源码详解》的支持,经过一年半的完善与努力,该书的纸质版《嵌入式网络那些事:LwIP协议深度剖析与实战演练》已由中国水利水电出版社出版,图书基本信息及相关宣传见:

http://www.wsbookshow.com/plus/view.php?aid=11531,在当当、淘宝、京东等均可购买。

    新书电子版的写作风格类似,把原来电子版的10万字扩展到了35万字,同时增加了很多实用内容,包括LwIP原理与代码结构,详细移植过程,常用编程案例,常见的使用问题与解决方法等等~~~同时,结合LwIP代码,对TCP/IP的原理讲解也更生动丰富了一些~~

    不做王婆鸟。。有兴趣的童鞋看上面的传送。。

标签:

lwip

tcp

api

it

分类: 嵌入式网络那些事

函数netconn_new用来创建一个新的连接结构。连接结构的类型可以选择为TCP或UDP等。函数结构原型如下所示,参数type描述了连接的类型,可以为NETCONN_TCP或NETCONN_UDP等,这里都以TCP作为讨论的对象。

struct netconn* netconn_new(enum netconn_type type)

该函数首先调用netconn_alloc函数分配并初始化一个netconn结构。初始化的过程包括设置netconn结构类型字段,同时为该结构的op_completed创建一个信号量、recvmbox字段创建一个接收邮箱。奇怪的是netconn_alloc函数并不是在文件api_lib.c文件中,而是在api_msg.c中,凌乱!接下来函数netconn_new会构建一个api_msg消息,该消息要求内核执行函数do_newconn,最后函数tcpip_apimsg用来将消息包装成tcpip_msg结构并发送出去。tcpip_thread函数解析该消息并调用函数do_newconn,do_newconn根据参数的类型最终调用函数tcp_new创建一个TCP控制块。tcpip_apimsg会阻塞在一个信号量上,直至do_newconn释放该信号量。

函数netconn_delete用来删除一个连接结构netconn。与前面的流程相同,它通过

标签:

lwip

tcp

api

it

分类: 嵌入式网络那些事

现在有必要来看看前面一直提到的内核协议栈进程是什么样子的。这个函数叫tcpip_thread,其源码如下,其中去掉了不相关的编译选项和非重点讨论部分。

static void tcpip_thread(void *arg)

{

       struct tcpip_msg *msg;

       sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);//创建IP分片重装超时事件

       sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);// 创建ARP超时事件

       while (1) {                          // 进程循环

           sys_mbox_fetch(mbox, (void *)&msg);    // 阻塞在邮箱上接收要处理的消息

           switch (msg->type) {  // 判断消息类型

 &nb

标签:

lwip

tcp

重传

nagle

it

分类: 嵌入式网络那些事

前面介绍过,在收到一个失序的报文段时,该报文段会被挂接到ooseg队列上,同时向发送端返回一个ACK(期待的下一个字节),很明显,这个ACK一定是个重复的ACK,且这个重复的ACK被发送出去的时候不会有任何延迟。接收端利用该重复的ACK,目的在于让对方知道收到一个失序的报文段,并告诉对方自己希望收到的序号。

但是在发送方看来,它不可能知道一个重复的ACK是由一个丢失的报文段引起的,还是由于仅仅出现了几个报文段的重新排序引起。因此我们需要等待少量重复的ACK到来。假如这只是一些报文段的重新排序,则在重新排序的报文段被处理并产生一个新的ACK之前,只可能产生1 ~ 2个重复的ACK。如果一连串收到3个或3个以上的重复ACK,就非常可能是一个报文段丢失了。于是我们就重传丢失的数据报文段,而无需等待超时定时器溢出。这就是快速重传算法。在上节讲超时重传时说到,当超时发生后,ssthresh会被设置为有效发送窗口的一半,而cwnd被设置为一个报文段大小,即执行的是慢启动算法。而在这里,当执行完快速重传后,接下来执行的不是慢启动算法而是拥塞避免算法,这就是所谓的

标签:

lwip

tcp

发送方

接收方

算法

分类: 嵌入式网络那些事

这节讨论慢启动算法与拥塞避免算法。版权声明:下面这几段的内容大部分来自于《TCP/IP详解,卷1:协议》,若有雷同,纯非巧合!

假如按照前面所有的讨论,发送方一开始便可以向网络发送多个报文段,直至达到接收方通告的窗口大小为止。当发送方和接收方处于同一个局域网时,这种方式是可以的。但是如果在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题。中间路由器缓存分组,可能消耗巨大的时间和存储器空间开销,这样TCP连接的吞吐量会严重降低。

要解决这种问题,TCP需要使用慢启动(slow start)算法。该算法通过观察新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为 1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。

拥塞窗口是发送方使用的流量控制,而通告窗

标签:

lwip

tcp

超时

重传

it

分类: 嵌入式网络那些事

在TCP两端交互过程中,数据和确认都有可能丢失。TCP通过在发送时设置一个定时器来解决这种问题。如果当定时器溢出时还没有收到确认,它就重传该数据。对任何TCP协议实现而言,怎样决定超时间隔和如何确定重传的频率是提高TCP性能的关键。

这节讲解TCP的超时重传机制,TCP控制块tcp_pcb内部的相关字段为rtime、rttest、rtseq、sa、sv、rto、nrtx,太多了,先不要晕!

与超时时间间隔密切相关的是往返时间(RTT)的估计。RTT是某个字节的数据被发出到该字节确认返回的时间间隔。由于路由器和网络流量均会变化,因此RTT可能经常会发生变化,TCP应该跟踪这些变化并相应地改变其超时时间。

在某段时间内发送方可能会连续发送多个数据包,但发送方只能选择一个发送包启动定时器,估计其RTT值,另外,一个报文段被重发和该报文的确认到来之前不应该更新估计器。协议中利用一些优化算法平滑RTT的值,并根据RTT值设置RTO的值,即下一个数据包的重传超时时间。

先来看看超时重传机制是怎样实现的,再来重点介绍与R

标签:

lwip

tcp

字段

队列

链表

分类: 嵌入式网络那些事

这节从tcp_receive函数入手,逐步深入了解控制块各个字段的意义以及整个TCP层的运行机制,足足600行,神想吐血。源码注释的该函数功能为:检查收到的数据段是不是对已发数据段的确认,如果是,则释放相应发送缓冲中的数据;接下来,如果该数据段中有数据,应将数据挂接到控制块的接收队列上(pcb->ooseq)。如果数据段同时也是对正在进行RTT估计的数据段的确认,则RTT计算也在这个函数中进行。我晕,陷入了恶性循环。越看越难,越看越说不清,TCP的东西太多了。要讲清楚tcp_receive还得说清楚tcp_enqueue,不管了,先硬着头皮写下去!源码注释对该函数的功能描述很简单:将数据包或者连接的控制握手包放到tcp控制块的发送队列上。这个函数的原型为

err_t

tcp_enqueue( struct tcp_pcb *pcb, void *arg, u16_t len,u8_t flags,

u8_t apiflags,u8_t *optdata, u8_t optlen )

其中有几个重要的输入参数:pcb是相应连接的TCP控制块;arg是要发送的数据的指针;len是要发送的数据的长度,以字节为单位;flags

  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有