用lwip发送大量数据时,遇到的问题解答记录

用lwip发送大量数据时,遇到的问题解答记录;

  这两天,师弟在两台电脑上搭建了lwipwin32通信平台,目的是能够不断发送一幅幅图片图像大小为1280*720大小的图片。如果不考虑压缩情况且是256色即1字节,这样大小的一幅图片需要发送大约发送1M字节。具有李逵性格的山东师弟目标是:客服端发送一次请求,服务器端能够通过TCP方式一次发送大约1M的数据;我震惊了, 我说你可知道MTU(1500)的上限,以及内存的考虑。当然让每次发送一次数据量尽可能大,这种想法是好的;看着他每天哀声叹气,临近毕业的我深表同情,另外我对lwip也蛮干感兴趣的,一直都想玩玩这个技术,但一直没有机会,言归正传,稍微介绍下lwip。

                          

1、概述:lwip是轻量型的TCP/IP实现,只需10几KB的RAM和40几KB的ROM就能够跑起来,适合应用于嵌入式设备的网络通信。有牛人Adam Dunkels发明,提供给用户上那种接口

RAW TCP/IP、Squential API, BSD API(也就是常说的socket编程),前者编程稍微复杂点,协议和应用程序在一个进程里面,但是效率高。中者首先要操作系统的支持,但是一旦实现了操作系统模拟层的实现,编程妥妥的。后者是为了符合人们使用socket编程的习惯而模拟的socket实现。所以王者乃RAW TCP/IP。网上提供了源码的下载,现在已经更新至1.4.x(doc 开发与移植指导文档, src 源码 test 测试例子),doc里面罗列了RAW TCP/IP的开发接口函数。

2、demo搭建:

服务器端:
0 open_tap()里面选择适合的网卡

1 开辟新进程 sys_thread_new("http thread",server_init, NULL, 0, 0);

2 设定网址和默认路由器 netif_add(&netif, &ip, &mask, &gw, NULL, ethernetif_init, tcpip_input);

3 在server_init里面 分别调用pcb = tcp_new(); tcp_bind(pcb, IP_ADDR_ANY, 8000);pcb = tcp_listen(pcb);

tcp_accept(pcb, server_accept); tcp_poll(pcb, tcp_poll, 10);// 每5s执行一次tcp_poll;

4 当有新的连接到来时,即有新的数据接收时, server_accept 就好调用。在server_accept里面注册调用函数tcp_recv(pcb, server_recv);

此后每次新的数据过来时,就会调用server_recv函数。

5 在函数 static err_t server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)里面对pbuf进行分析,要注意p->next的值。

并且借鉴其他资料知道立即要调用tcp_recevd;很神秘的告诉你要调用否则不好使。后面这里有重要发现!!!。

6 调用pbuf_free函数,来释放pbuf;

客服端:

0~2步基本相同

3在client_init里面 分别调用pcb = tcp_new(); ret_val = tcp_connect(pcb, &dest, 8000, client_connected);

4 在client_connected里面发送连接请求。同时调用tcp_accept(pcb, client_recv)来注册数据接收处理回调函数。

5 在static err_t client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);对接收到的数据进行处理

类似server_recv。

搭建完成之后:师弟发现,在server_recv 函数里面只能够tcp_write 128个字节的数据。当发送大于128个数据时,就会只能够接收128个数据。

找了半天发现[2]是由opt.h里面的参数MSS 128决定的, 另外还要注意一个参数即TCP_SND_WND 256。

改变这两个后,发送比TCP_SND_WND小且比MSS大的数据时, 客户端可以接受到一次等于MSS的数据。

试了各种策略后,客户端只接收一次。我们甚至怀疑是回调函数注册一次只能使用一次的问题,都无效。

后面查询各种资料[3], 后面发现英文资料lwip-user给出了很多启示:

包括分析TCP segment(底层自行封装成不同packet,不带ACK) 和 IP segment(>MTU时,会带ACK)

了解了opt.h中:

MSS (the smaller, the better) 128
TCP_SND_BUF 256
MEM_SIZE (1600) HEAP SIZE 如果tcp_write 用COPY的方式时是需要设置较大的值;
TCP_SND_QUEUELEN  4*(TCP_SND_BUF/tcp_mss)(最小为除式的两倍);
MEMP_NUM_TCP_SEG: 至少跟上面一样大
MEMP_NUM_PBUF(16) --->32 

TCP_WND 2048 接收窗口,接收多少个数据包的问题。

今天通过wireshark抓包发现,1.15 端发送给了 1.111端后面多于MSS的数据报文,而且发送端一直发送MSS长度的报文,

可以得知应该是接收端的没有应答正确。前面提到的tcp_recevd调用的是tcp_ack函数。其实就是对接收到的数据进行应答

但是同时发现有一个好用的函数 tcp_ack_now(pcb),是直接发送应答,因为后面跟了tcp_output(pcb)函数。

并且要了解flags 设置 pcb->flags |= TF_NODELAY | TF_ACK_NOW; len = tcp_sndbuf(pcb);会告知剩余的buf的长度

并且多次发送的时候可以设置这个值,从而手动告知系统还有多少buf可以发送,从而实现了多次发送的操作。

改了这两处之后,实验可以发送32000个字节,不错。

 参考文档:

[1] http://lwip.wikia.com/wiki/Category:LwIP_Application_Developers_Manual
[2] https://groups.google.com/forum/#!topic/osdeve_mirror_tcpip_lwip/4GAbtXFwlvk

[3] 百度:关于LWIP协议栈连续多次tcp_write后失败的解决过程

[4] lwip design and implementation

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
LWIP是一款开源的网络协议栈,用于实现TCP/IP协议,支持在嵌入式系统中进行网络通信。在使用LWIP发送数据,如果出现卡死的情况,可能是以下几个原因导致的: 1. 网络连通性问题:首先需要确保网络连接正常,检查网络配置是否正确,包括IP地址、子网掩码、默认网关等。还应检查物理连接是否正常,如网线是否插好、物理设备是否正常工作。 2. 资源分配问题:发送数据,可能由于内存不足或者缓冲区溢出而导致卡死。可以通过增加内存大小或者调整缓冲区大小来解决该问题。另外,还要确保LWIP相关的资源得到正确的释放,避免内存泄漏等问题。 3. 软件逻辑错误:在代码编写过程中可能存在逻辑错误,导致LWIP发送数据出现死循环或者死锁等情况。可以通过检查代码逻辑,对可能的异常情况进行处理,避免程序陷入无法退出的状态。 4. 并发访问问题:如果多个任务同LWIP进行访问,可能导致竞争条件的发生,进而导致卡死。可以通过使用信号量、互斥锁等机制来保证资源的互斥访问,避免并发访问引起的问题。 5. 网络延迟或丢包问题:发送数据,可能由于网络延迟或丢包导致卡死。可以通过增加超机制,设置重传策略,或者对数据进行合理的分片等方式来解决这些问题。 综上所述,LWIP发送数据卡死可能是由于网络连通性问题、资源分配问题、软件逻辑错误、并发访问问题或者网络延迟丢包等原因所导致。需要根据具体情况进行排查,逐一解决问题,以确保LWIP发送数据的正常运行。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值