基于 LWIP 建立 TCP Server 与主机通信实验

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 与端口,点击连接,显示如下:

1319058-20180507211204207-1830867467.png

串口助手显示如下:

1319058-20180507211209392-938535647.png

附: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 网络实验,感谢

转载于:https://www.cnblogs.com/GyForever1004/p/9004999.html

实现STM32 FreeRTOS LwIP TCP服务器需要按照以下步骤进行操作: 1. 首先,需要配置LwIP和FreeRTOS。可以在STM32CubeMX中选择配置相应的组件,生成对应的代码和初始化函数。 2. 在代码中创建任务来处理TCP服务器。通过创建一个任务,可以将其分配给特定的核心,以处理TCP请求和响应。 3. 在任务中,首先需要进行LwIP和FreeRTOS的初始化。这样可以确保网络和操作系统的适当设置。需要调用lwip_init()和vTaskStartScheduler()函数。 4. 配置和创建TCP服务器的套接字。可以通过调用lwip_socket()函数创建一个TCP套接字,并使用lwip_bind()函数将其与特定的IP地址和端口绑定。 5. 通过调用lwip_listen()函数监听TCP套接字,等待客户端的连接。 6. 使用lwip_accept()函数接受客户端的连接请求,并获得一个新的套接字来处理与该客户端之间的通信。 7. 通过调用lwip_recv()和lwip_send()函数来接收和发送数据。可以使用这些函数接收来自客户端的数据,并发送响应数据给客户端。 8. 当与客户端的通信完成后,使用lwip_close()函数关闭套接字。 9. 循环进行步骤6-8,以处理其他客户端的连接和通信请求。 需要注意的是,STM32系列芯片的内存和处理能力有限,因此在编写代码时需要谨慎处理内存和资源的分配和释放,以确保程序的稳定性和性能。 总结:通过以上步骤,可以在STM32上使用FreeRTOS和LwIP实现TCP服务器,使其能够接受和处理客户端的连接和通信请求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值