LWIP 修改ip TCP发送 连接状态

应用:
1,不重启协议栈而远程修改本地ip等配置信息
2,tcp立即发送死循环  等待或者不用立即发送
3,与服务器连接状态判断方法 
a,发送利用发送成功回调函数判断(准确) 
b,lm3s网线连接状态寄存器(只能判断本地网线连接状态) 
c,TCP_PCB状态(不准确)  
d加入心跳机制

标题:[转载]协议网关lwip遇到的问题
2012-06-30 20:35:28

我使用TI芯片也有三年时间了,期间开发过低功耗无线传感器、无线转以太网网关等设备

现在讲一下用LM3S9B90芯片开发的无线转以太网网关设备中遇到的一些问题吧!

以太网用的是LWIP协议,这也是我第一次用32位机、第一次用ARM内核芯片、也是第一次与LWIP接触;

一开始我没有什么信心会做好,还好在TI例程及技术支持的帮助下,半年内将产品设计出来,并且相当稳定的运行。

这其间遇到最大的问题是TCP运行的问题:(前两个还是小问题,最后一个才是问题致命的所在)

   问题一,IP地址分配的问题;当时利用例程的办法,调用lwIPInit,和lwIPLocalIPAddrGet的办法获取IP地址,如果当时没接网线,或者路由器没有开启DHCP功能怎么办?

  最开始我采用重新调用以上函数的办法,进行处理,发现系统会复位;可见这个办法行不通,

  后来通过对LWIP的认识发现可以通过修改IP地址的方式,调用lwIPNetworkConfigChange函数,将IP地址设置成上次分配的IP,这样一来,只要设备能正常使用过一次,以后都不会有问题了!


  问题二,调用tcp_output函数,为了提高发送速度,在每次调用tcp_write函数前,先调用一次tcp_output函数;加了这个函数后, 短时间内使用是不会有问题的,但时间长了以后,发现系统也会复位;经过一系列的查找,终于锁定是tcp_output函数的问题,取消后,系统不再复位了


  问题三,网络异常的处理;在正常的使用过程中,会遇到设备端网线断开、服务器网线断开、服务器软件关闭、服务器电脑死机等等情况,其它几种都比较好判别,唯独服务器网线断开这种情况不能区分;

   服务器网线断开(设备与路由器链接是好的)的时候,设备不会得到异常报告,也不会有PHY寄存器标志,只能通过给服务器发送数据时,判断是否有发送完成回调函数;如果没有回调函数,服务器链路一定有问题。

   一旦出现服务器网线断开的情况,设备的LWIP中的pcb->state一直处理链接成功的状态,而服务器在一定时间后会删除这条链接,也就是 说,超过一定的时间后,设备与服务器之间的链接状态是不一样的;同时,设备一直处于虚假链接,不会重新与服务器建立链接;

   最后导致,服务器与设备是永远也不可能建立链接。

   这个问题困扰了我们好长一段时间,而且是在客户的使用中才发现,客户的压力可想而知。

   还好,经过一段时间的排查,终于找到问题的所在、以及解决问题的办法:

   效仿服务器的办法,在经过一段超时后,主动断开与服务器(确切来说是路由器)的链接,给服务器发送close命令;发完后,pcb进入 FIN_WAIT_1状态和FIN_WAIT_2状态,如果期间链接上,服务返回ACK确认关闭链接, 否则超时后,设备自动进入CLOSE状态;最终达到两端的链接状态一致;一旦链路恢复正常,设备与服务器就能再次正常建立链接。这个问题总算是解决了。

   设备运行到现在已经快半年了,目前为止,还没有发现什么问题。


lwip源码分析
http://wenku.baidu.com/view/84a478a2284ac850ad024263.html


lwip状态


状态:描述
  CLOSED:无连接是活动的或正在进行
  LISTEN:服务器在等待进入呼叫
  SYN_RECV:一个连接请求已经到达,等待确认
  SYN_SENT:应用已经开始,打开一个连接
  ESTABLISHED:正常数据传输状态
  FIN_WAIT1:应用说它已经完成
  FIN_WAIT2:另一边已同意释放
  ITMED_WAIT:等待所有分组死掉
  CLOSING:两边同时尝试关闭
  TIME_WAIT:另一边已初始化一个释放
  LAST_ACK:等待所有分组死掉


这次主要讲解tcp函数的发送部分。
我们的流程是tcp_write->tcp_enqueue->tcp_output.
1.         tcp_write函数
这个函数很简单,首先判断此时的pcb是否进入了可发送数据的状态,这些状态有ESTABLISHED, CLOSE_WAIT, SYN_SENT, SYN_RECD,(还有一些在状态机里面见到的状态,如LISTEN,FIN_WAIT1,FIN_WAIT2,CLOSED,CLOSING等则要么处于连接还未建立,要么是连接已关闭的状态,顶多再能发个ack包,不能再发数据了)如果是处于这些有效状态,则调用tcp_enqueue函数。
 
2.        tcp_enqueue函数
tcp_enqueue通过把上层需要发送的数据包放在队列中,最后一起调用tcp_output来提高效率。看一下参数:
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, u8_t flags, u8_t apiflags, u8_t optflags)
pcb:当前连接的pcb,arg:需要发送的数据,len:数据长度,flags:tcp首部的标志位。apiflags:TCP_WRITE_FLAG_COPY(说明空间需要分配并且拷贝至pbuf,否则则说明数据来自静态的存储器,比如rom,不需要拷贝)TCP_WRITE_FLAG_MORE。
 
看代码流程:
A.首先保存一些变量,比如此次传输数据的长度,要传输的数据的地址
B. 将所要传输的数据切割成一个个tcp_seg结构体,最大长度不超过一个mss,如果小于就是一个tcp_seg。很奇怪,第一个seg是存放在queue指针的队列上,其余的是存放在useg指针的队列上,并且useg始终指向最后一个seg结构体。
C. 完成了数据切割,我们现在需要把这些seg加入到pcb->unsend队列中。但是这段代码很奇怪,不知道是我看错了还是有bug?但理论上不会有这么明显的bug啊。
 
3.        tcp_output函数
在tcp_enqueue函数中已经把我们要发送的数据放在了pcb的unsend链表上了。接下来我们就是要将这些数据发送出去。看流程:
A.      首先过滤flags标志位为立即响应并且没有携带数据的调用,直接tcp_send_empty_ack(pcb);(因为不是每个对tcp_output的调用都是经过tcp_enqueue)
B.       利用while循环对unsend链表上的seg一个个发送或跳过。
a)        跳过:对每个seg包会首先用nagle算法做一下检测,如果满足nagle条件,则会跳过这个数据包不做任何操作,留置合并一起发送。具体的条件可参考tcp概念部分的文章。
b)        发送:对于一般的seg包,则先置ack标志位,然后调用tcp_output_segment(seg, pcb);发送。
 
4.        关于tcp_output中的tcp_send_empty_ack(pcb);
a)        这里面的操作比较简单,主要是建立pbuf,然后建立tcp的首部与pbuf的联系,tcphdr = p->payload,然后填充tcphdr,传递给ip层。(调用ip_output函数)
5.        关于tcp_output中的tcp_output_segment(seg, pcb);
a)        同tcp_send_empty_ack(pcb);类似,不是很复杂。



原帖地址:http://www.deyisupport.com.sixxs.org/question_answer/f/23/t/5240.aspx?PageIndex=8作者:hasten lin on 2012-1-12 0:15


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: LWIP(Lightweight IP)是一个用于嵌入式系统的轻量级TCP/IP协议栈。使用LWIP库可以方便地实现通过串口发送数据到TCP服务器的功能。 首先,需要初始化LWIP协议栈,并创建一个TCP客户端连接。通过配置串口通信参数,确保串口与设备之间的数据传输正常。然后,使用串口接收数据的中断函数,获取待发送的数据,将其存储在一个缓冲区中。 在TCP客户端连接建立后,可以通过LWIP库提供的API函数,将缓冲区中的数据发送TCP服务器。通过调用lwip_send()函数,将数据写入到TCP发送缓冲区中,并通过TCP协议栈将数据发送到远程服务器。同时,可以使用lwip_recv()函数接收服务器返回的数据,并进行相关处理。 在发送数据时,应注意TCP发送缓冲区的空闲空间。如果发送速度过快,发送缓冲区可能会满,导致数据丢失或发送失败。因此,可以使用lwip_sndbuf()函数查询缓冲区剩余的可用空间,以便根据情况适时调整发送速度。 另外,为了提高数据传输的可靠性,可以使用LWIP的重传机制。如果发送的数据在网络中丢失或未收到响应,TCP协议栈会自动重传丢失的数据包,以确保数据的可靠传输。 需要注意的是,在使用LWIP发送数据时,应遵循TCP协议的相关规定,例如TCP的流控制和拥塞控制等机制,以避免网络拥塞和数据丢失的问题。 总之,通过使用LWIP库,可以方便地实现通过串口发送数据到TCP服务器的功能,并提供可靠的数据传输机制,以满足嵌入式系统中对网络通信的需求。 ### 回答2: LWIP是一个轻量级的开源的TCP/IP协议栈。它提供了一种在嵌入式系统中实现TCP/IP网络通信的解决方案。 在LWIP中,实现串口数据通过TCP发送的过程需要以下步骤: 首先,需要配置串口的相关参数,例如波特率、数据位数、停止位等。可以通过调用LWIP提供的接口函数来进行配置。 然后,需要创建一个TCP连接。可以通过调用lwip_tcp_new函数来创建一个新的TCP连接,并将其绑定到一个特定的端口号上。 接下来,需要绑定一个回调函数到TCP连接上,用于处理接收到的数据。当有数据从串口读取并发送TCP连接上时,回调函数将被自动触发。 在回调函数中,可以通过调用lwip_tcp_write函数将接收到的串口数据写入到TCP连接中。也可以通过调用lwip_tcp_output函数将数据发送出去。需要注意的是,在发送数据之前,应确保TCP连接已经建立成功。 最后,需要调用lwip_tcp_close函数来关闭TCP连接,释放相关资源。 需要特别注意的是,由于LWIP是一个单线程的协议栈,所以在实际使用中可能需要使用操作系统提供的多线程功能,例如使用操作系统提供的线程库将串口读取和TCP发送的过程分别放在不同的线程中。 总的来说,使用LWIP实现串口数据通过TCP发送,需要配置串口参数、创建TCP连接、绑定回调函数、读取串口数据并发送TCP连接中,并最后关闭TCP连接。以上是一个简单的描述,具体实现还需要根据具体的应用场景和需求进行相应的定制。 ### 回答3: 使用lwIP(轻型IP协议栈)库,可以很方便地实现通过串口发送数据的TCP连接。 首先,需要在代码中引入lwIP库,并初始化lwIP协议栈。具体的初始化过程可以参考lwIP官方文档或者示例代码。 在初始化完成后,需要创建一个TCP服务器或者客户端连接。创建TCP服务器可以使用`tcp_new()`函数,指定一个回调函数来处理接收到的数据。创建TCP客户端可以使用`tcp_connect()`函数,指定要连接的服务器地址和回调函数。回调函数可以在接收到数据时进行相应的处理操作。 若要通过串口发送数据,可以使用标准串口库,例如STM32的HAL库中的函数`HAL_UART_Transmit()`。在接收到需要发送的数据后,可以通过调用该函数将数据通过串口发送出去。 具体的步骤如下: 1. 初始化lwIP协议栈。 2. 创建TCP服务器或者客户端连接,并指定回调函数。 3. 接收到需要发送的数据后,调用串口发送函数将数据发送出去。 4. 回调函数中可以针对接收到的数据进行处理,例如将数据缓存起来或者做其他操作。 需要注意的是,lwIP是一个基于事件驱动的协议栈,需要在主循环中定期调用`tcpip_input()`函数来处理网络数据。此外,还需要配置lwIPIP地址、子网掩码、默认网关等网络参数,以便能够正常连接到网络。 总之,使用lwIP库可以方便地实现通过串口发送数据的TCP连接

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值