LWIP 版本:2.0.3

上一篇文章是写如何将 LWIP 移植到板子上,今天晚上记录基于 LWIP 实现与主机的网络通信。

先是打开了原子的实验例程,大概浏览了一遍,觉得 TCP 网络网络通信也就是那么一些套路。什么 创建、配置、绑定、监听、accept ....,果断复制源文件到工程路径下,调整头文件包含直至编译无误。将 tcp_server_init( ) 加入到 main 中,下载测试,果然出现问题。 ping 都 ping 不通了,尴尬.....

问题解决过程:

出问题了是好事,可以更加深入的了解 LWIP(强行安慰自己一波)。遂加入 printf 输出查找问题根源,最后追踪到 netconn_new 内的 netconn_alloc 函数处,解决办法如下:

本以为是其内部调用的函数 memp_malloc 申请内存失败。进入函数内部开始阅读源码,发现 MEMP_MEM_MALLOC 宏没有被打开,误理解为没有启用该宏就不会成功申请到内存,后来经测试发现并不是它的问题,官方注释如下:

/**
* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
* speed (heap alloc can be much slower than pool alloc) and usage from interrupts
* (especially if your netif driver allocates PBUF_POOL pbufs for received frames
* from interrupt)!
* ATTENTION: Currently, this uses the heap for ALL pools (also for private pools,
* not only for internal pools defined in memp_std.h)!
*/

如果不启用该宏,LWIP 内核会直接从 lwip pool 中取出相应的结构,pool 的内存已经在 LWIP 内核运行前申请完毕,官方推荐不启用该宏,因为 speed (heap alloc can be much slower than pool alloc)。

后来继续查找问题所在,发现一个致命问题:netconn_alloc 函数内调用的创建邮箱函数 sys_mbox_new 没有为其参数(一个邮箱指针)分配内存,这是移植时残留的问题,终于找上头来。通过 LWIP 内核中提供的 mem_malloc/mem_free 函数解决了这个问题,如下所示:

err_t sys_mbox_new( sys_mbox_t *mbox, int size)
{
*mbox = mem_malloc(sizeof(TQ_DESCR));
.....
} void sys_mbox_free(sys_mbox_t * mbox)
{
.......
mem_free(*mbox);
*mbox=NULL;
}

重新编译工程,下载到板子,完美运行!

运行结果:

打开网络助手,配置好 tcp server 与端口,点击连接,显示如下:

串口助手显示如下:

附:lwip_server.c 源码

#include "tcp_server.h"
#include "lwip/opt.h"
#include "lwip_app.h"
#include "lwip/sys.h"
#include "lwip/api.h"
#include "delay.h"
#include "string.h" u8 tcp_server_recvbuf[TCP_SERVER_RX_BUFSIZE];
static const u8 tcp_server_sendbuf[] = "Welcome to connect to the LWIP server.\r\n";
u8 tcp_server_flag = 0xff;
#define TCPSERVER_PRIO 6
#define TCPSERVER_STK_SIZE 300
OS_STK TCPSERVER_TASK_STK[TCPSERVER_STK_SIZE]; static void tcp_server_thread(void *arg)
{
OS_CPU_SR cpu_sr;
u32 data_len = 0;
struct pbuf *q;
err_t err,recv_err;
u8 remot_addr[4];
struct netconn *conn, *newconn;
static ip_addr_t ipaddr;
static u16_t port; LWIP_UNUSED_ARG(arg); /* 创建 TCP、绑定、监听 */
conn = netconn_new(NETCONN_TCP);
netconn_bind(conn,IP_ADDR_ANY,TCP_SERVER_PORT);
netconn_listen(conn); /* 禁止阻塞线程,等待时间为 10ms */
conn->recv_timeout = 10; while (1)
{
err = netconn_accept(conn,&newconn);
if(err==ERR_OK) newconn->recv_timeout = 10; if (err == ERR_OK)
{
struct netbuf *recvbuf; /* 获取到客户端的 IP 及端口号 */
netconn_getaddr(newconn,&ipaddr,&port,0); remot_addr[3] = (uint8_t)(ipaddr.addr >> 24);
remot_addr[2] = (uint8_t)(ipaddr.addr >> 16);
remot_addr[1] = (uint8_t)(ipaddr.addr >> 8);
remot_addr[0] = (uint8_t)(ipaddr.addr);
printf("主机:%d.%d.%d.%d 连接上服务器,主机端口号为:%d\r\n",remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3],port); while(1)
{
if((tcp_server_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA)
{
/* 向客户端发送消息 */
err = netconn_write(newconn,tcp_server_sendbuf,strlen((char*)tcp_server_sendbuf),NETCONN_COPY);
if(err != ERR_OK)
{
printf("发送失败\r\n");
}
tcp_server_flag &= ~LWIP_SEND_DATA;
} /* 接收消息 */
if((recv_err = netconn_recv(newconn,&recvbuf)) == ERR_OK)
{
OS_ENTER_CRITICAL();
memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE); /* 将缓冲区信息转存到自定义空间 */
for(q=recvbuf->p;q!=NULL;q=q->next)
{
if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len))
memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));
else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
data_len += q->len;
if(data_len > TCP_SERVER_RX_BUFSIZE) break;
}
OS_EXIT_CRITICAL();
data_len=0;
printf("%s\r\n",tcp_server_recvbuf);
netbuf_delete(recvbuf); /* 断开连接 */
}else if(recv_err == ERR_CLSD)
{
netconn_close(newconn);
netconn_delete(newconn);
tcp_server_flag = 0xff;
printf("主机:%d.%d.%d.%d 断开与服务器的连接\r\n",remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3]);
break;
}
}
}
delay_ms(10);
}
} INT8U tcp_server_init(void)
{
INT8U res=0;
OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); /* 创建 TCP 服务器任务 */
res = OSTaskCreate(tcp_server_thread,(void*)0,(OS_STK*)&TCPSERVER_TASK_STK[TCPSERVER_STK_SIZE-1],TCPSERVER_PRIO);
OS_EXIT_CRITICAL(); return res;
}

本篇文章参考自原子的 LWIP 网络实验,感谢

基于 LWIP 建立 TCP Server 与主机通信实验的更多相关文章

  1. Python之tcp server模拟Http通信

    1.python tcp server代码: import socket def main(): tcp_server_socket = socket.socket(socket.AF_INET, s ...

  2. swoole之建立 tcp server

    一.swoole的安装 参照官网:https://wiki.swoole.com/wiki/page/6.html 二.代码部分 服务端: <?php $host = "127.0.0 ...

  3. 【iCore3 双核心板_FPGA】实验十六:基于SPI总线的ARM与FPGA通信实验

    实验指导书及代码包下载: http://pan.baidu.com/s/1hs6lDdi iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...

  4. 【iCore3 双核心板_FPGA】实验十七:基于I2C总线的ARM与FPGA通信实验

    实验指导书及代码包下载: http://pan.baidu.com/s/1dFqddMp iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...

  5. ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  6. 基于Linux的TCP网络聊天室

    1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...

  7. CentOS7 基于 subversion 配置 SVN server

    由于 Window Server 环境下,VisualSVN Server Community 版本只支持 15 个同时在线用户,所以彻底放弃 Windows Server,在 Linux Serve ...

  8. 【Java TCP/IP Socket】基于NIO的TCP通信(含代码)

    NIO主要原理及使用 NIO采取通道(Channel)和缓冲区(Buffer)来传输和保存数据,它是非阻塞式的I/O,即在等待连接.读写数据(这些都是在一线程以客户端的程序中会阻塞线程的操作)的时候, ...

  9. Python的网络编程[0] -> socket[2] -> 利用 socket 建立 TCP/UDP 通信

    Socket 目录 socket 的 TCP/IP 通信基本建立过程 socket 的 UDP 通信基本建立过程 socket 的 UDP 广播式通信基本建立过程 socket 的多线程通信建立过程 ...

随机推荐

  1. 2019浙大校赛--E--Potion(签到水题)

    一丢丢思维就ok 题目大意: 魔法师要煮药,有n个等级的药,所需要的药物为a1,a2...an,意为第n级需要多少药物,下一行为库存的不同等级药物,药物可降级使用不可升级. 思路:从高级药物开始解,把 ...

  2. 多个子域名前端网站调用同一个webAPI时session混用问题

    session机制: 当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,如果已包含一个sess ...

  3. influence maximization

    Robust Influence Maximization 首先简要介绍一下这个问题:在一个社交网络图中寻找固定数量的节点,使得这些节点对所有节点的影响值尽可能的大.这个问题由于在病毒式营销,谣言监控 ...

  4. easyui 日期控件,选择日期小于等于当前日期,开始日期小于等于结束日期

    转载出处:http://blog.csdn.net/u013755149/article/details/76613028 $(function(){ $('#start_date').datebox ...

  5. Python学习笔记-函数基础

    函数基础 定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可 为什么使用函数:减少重复代码.使程序变的可扩展使程序变得易维护 1.定义一个函数 #定 ...

  6. MySQL9:索引实战

    索引 无论是面试,还是实际工作中,对于一个Java程序员来说,数据库优化是避不开的一个技术点,关于数据库的优化,在性能达不到要求的情况下,我大致给出以下几个方向: (1)优化表结构,对常用字段和非常用 ...

  7. redis主从架构,分片集群详解

    写在前面:这篇笔记有点长,如果你认真看完,收获会不少,如果你只是忘记了相关命令,请翻到末尾. redis的简单介绍: 一个提供多种数据类类型储存,整个系统都在内存中运行的, 定期通过异步的方式把数据刷 ...

  8. Java进程和线程关系及区别

    1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基 ...

  9. 微信小程序实现显示和隐藏控件-头像-取值-bindblur事件

    微信小程序实现显示和隐藏控件 .wxml: <view class=" {{showOrHidden?'show':'hidden'}}"></view> ...

  10. C#退出窗体的总结方法

    一.关闭窗体 在c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.ExitThread(); System.En ...