基於 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.\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 網絡實驗,感謝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM