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

    串口助手显示如下:

    附: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.
    ";	
    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
    ",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("发送失败
    ");
    					}
    					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
    ",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 断开与服务器的连接
    ",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 网络实验,感谢

  • 相关阅读:
    素数筛的2种方法
    c++含结构体的sort()使用
    构建c++二维vector
    c语言输入单字符避免回车的四种方法
    menset()在c++中的作用
    杭电oj hud1092 1093 活用EOF&n--
    EOF在while(scanf("%d",&n))中的作用
    KMP算法
    图解HTTP(3)
    图解HTTP(2)
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/9004999.html
Copyright © 2020-2023  润新知