LWIP的NETCONN接口

NETCONN接口簡介

NETCONN API 使用了操作系統的 IPC 機制, 對網絡連接進行了抽象,使用同一的接口完成UDPTCP連接

NETCONN API接口是在RAW接口基礎上延申出來的一套API接口

NETCONN實現原理

2.1NETCONN控制塊

2.2NETCONN收發的數據管理

2.3NETCONN實現原理

NETCONN控制塊

struct netconn 
{ 
enum netconn_type type; 	/* 連接類型, TCP UDP 或者 RAW */ 
enum netconn_state state;	/* 當前連接狀態 */ 
union 					/* 內核中與連接相關的控制塊指針 */ 
{ 
struct ip_pcb *ip; 	/* IP 控制塊 */ 
struct tcp_pcb *tcp; 	/* TCP 控制塊 */ 
struct udp_pcb *udp; 	/* UDP 控制塊 */ 
struct raw_pcb *raw; 	/* RAW 控制塊 */ 
} pcb; 
err_t last_err; 			/* 此連接上最新的錯誤 */ 
sys_sem_t op_completed; 	/* 用于 API 同步的信號量 */ 
sys_mbox_t recvmbox; 		/* 接收數據的郵箱 */ 
...后面還有部分代碼... 
}

控制塊實現

1、統一編程接口

2、統一連接結構

3、可使用IPC機制

4、錯誤提示

什么是IPC機制

個人理解:就是系統的OS的機制

NETCONN收發的數據管理

struct netbuf 
{/* 發送方的數據包 */ 
struct pbuf *p, *ptr; 
/* 發送方的IP地址 */ 	
ip_addr_t addr; 
/* 發送方的端口號 */  		
u16_t port; 			
};

①p ptr 都指向 pbuf 鏈表,不同的是 p 一直指向 pbuf 鏈表的第一個 pbuf 結構, 而 ptr 可能指向鏈表中其他的位置,netbuf_next()netbuf_first()操作 ptr 字段。

addr port 字段用來記錄數據發送方的 IP 地址和端口號,netbuf_fromaddr netbuf_fromport 這兩個宏定義用于返回 addr port 這兩個字段。

收發數據管理結構圖

NETCONN實現原理

先創建一個netconn控制塊? ?再創建一個udp控制塊? 然后通過udp接口接收數據

等效流程圖

這里的發送郵箱,是os的郵箱

存在發送郵箱,那么就有接收郵箱

此時產生新的流程,得到數據如下;(如下:前三個,類似于初始化位置)

NETCONNRAW接口的區別

區別

NETCONN API

RAW API

數據接收

回調型+IPC機制

回調型(用戶編寫)

工作機制

封裝內核函數,提供統一接口

操作內核函數實現

效率性

較高

易用性

高(少接觸內核代碼)

低(了解內核代碼)

開發環境

OS

OS/OS

……

……

……

netconn類似于STM32的標準庫開發,而RAW接口類似于STM32寄存器開發

NETCONN相關函數

NETCONN API

描述

netconn_new

創建netconn控制塊

netconn_delete

刪除控制塊

netconn_bind

本地IP地址與端口號綁定

netconn_connect

與目的IP地址和端口號綁定

netconn_disconnect

斷開連接

netconn_listen

監聽連接(只在TCP服務器)

netconn_accept

獲取一個TCP連接(只在TCP服務器)

netconn_recv

recvmbox 郵箱中接收數據包(UDPTCP可用)

netconn_send

發送數據(UDP

netconn_write

發送數據(TCP

……

……

NETCONN接口UDP連接配置流程

1、netconn_new

創建NETCONN控制塊(udp_new/udp_recv

2、netconn_bind

綁定本地IP地址和端口號

3、netconn_connect

綁定目的IP地址和目的端口號并且插入PCB鏈表

4、send/recv

調用NETCONN相關函數發送和接收數據

PS:netbuf用來管理pbuf的,所以netbuf,可以作用到pbuf數據

主代碼

\Middlewares\lwip\lwip_app\lwip_demo.c


/* 這個必須填寫正確,遠程IP地址 */
#define DEST_IP_ADDR0               192
#define DEST_IP_ADDR1               168
#define DEST_IP_ADDR2                 1
#define DEST_IP_ADDR3               111#define LWIP_DEMO_RX_BUFSIZE         200   /* 定義最大接收數據長度 */
#define LWIP_DEMO_PORT               8080  /* 定義連接的本地端口號 *//* 接收數據緩沖區 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 
/* 發送數據內容 */
char *g_lwip_demo_sendbuf = "ALIENTEK DATA\r\n";
/* 數據發送標志位 */
uint8_t g_lwip_send_flag;
extern QueueHandle_t g_display_queue;   /* 顯示消息隊列句柄 */
/*** @brief       lwip_demo實驗入口* @param       無* @retval      無*/
void lwip_demo(void)
{err_t err;static struct netconn *udpconn;static struct netbuf  *recvbuf;static struct netbuf  *sentbuf;ip_addr_t destipaddr;uint32_t data_len = 0;struct pbuf *q;BaseType_t lwip_err;/* 第一步:創建udp控制塊 */udpconn = netconn_new(NETCONN_UDP);/* 定義接收超時時間 */udpconn->recv_timeout = 10;if (udpconn != NULL)                                        /* 判斷創建控制塊釋放成功 */{/* 第二步:綁定控制塊、本地IP和端口 */err = netconn_bind(udpconn, IP_ADDR_ANY, LWIP_DEMO_PORT);/* 構造目的IP地址 */IP4_ADDR(&destipaddr, DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3);/* 第三步:連接或者建立對話框 */netconn_connect(udpconn, &destipaddr, LWIP_DEMO_PORT);  /* 連接到遠端主機 */if (err == ERR_OK)                                      /* 綁定完成 */{while (1){/* 第四步:如果指定的按鍵按下時,會發送信息 */if ((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA){sentbuf = netbuf_new();netbuf_alloc(sentbuf, strlen((char *)g_lwip_demo_sendbuf));memcpy(sentbuf->p->payload, (void *)g_lwip_demo_sendbuf, strlen((char *)g_lwip_demo_sendbuf));err = netconn_send(udpconn, sentbuf);               /* 將netbuf中的數據發送出去 */if (err != ERR_OK){printf("發送失敗\r\n");netbuf_delete(sentbuf);                         /* 刪除buf */}g_lwip_send_flag &= ~LWIP_SEND_DATA;                  /* 清除數據發送標志 */netbuf_delete(sentbuf);                             /* 刪除buf */}/* 第五步:接收數據 */netconn_recv(udpconn, &recvbuf);if (recvbuf != NULL)                                    /* 接收到數據 */{memset(g_lwip_demo_recvbuf, 0, LWIP_DEMO_RX_BUFSIZE); /* 數據接收緩沖區清零 */for (q = recvbuf->p; q != NULL; q = q->next)        /* 遍歷完整個pbuf鏈表 */{/* 判斷要拷貝到UDP_DEMO_RX_BUFSIZE中的數據是否大于UDP_DEMO_RX_BUFSIZE的剩余空間,如果大于 *//* 的話就只拷貝UDP_DEMO_RX_BUFSIZE中剩余長度的數據,否則的話就拷貝所有的數據 */if (q->len > (LWIP_DEMO_RX_BUFSIZE - data_len)) memcpy(g_lwip_demo_recvbuf + data_len, q->payload, (LWIP_DEMO_RX_BUFSIZE - data_len)); /* 拷貝數據 */else memcpy(g_lwip_demo_recvbuf + data_len, q->payload, q->len);data_len += q->len;if (data_len > LWIP_DEMO_RX_BUFSIZE) break;     /* 超出TCP客戶端接收數組,跳出 */}data_len = 0;                                       /* 復制完成后data_len要清零 */lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);if (lwip_err == errQUEUE_FULL){printf("隊列Key_Queue已滿,數據發送失敗!\r\n");}netbuf_delete(recvbuf);                             /* 刪除buf */}   else vTaskDelay(5);                                     /* 延時5ms */vTaskDelay(10);}}else printf("UDP綁定失敗\r\n");}else printf("UDP連接創建失敗\r\n");
}

User\freertos_demo.c


/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define START_TASK_PRIO         5           /* 任務優先級 */
#define START_STK_SIZE          128         /* 任務堆棧大小 */
TaskHandle_t StartTask_Handler;             /* 任務句柄 */
void start_task(void *pvParameters);        /* 任務函數 *//* LWIP_DEMO 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define LWIP_DMEO_TASK_PRIO     11          /* 任務優先級 */
#define LWIP_DMEO_STK_SIZE      1024        /* 任務堆棧大小 */
TaskHandle_t LWIP_Task_Handler;             /* 任務句柄 */
void lwip_demo_task(void *pvParameters);    /* 任務函數 *//* LED_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define LED_TASK_PRIO           10          /* 任務優先級 */
#define LED_STK_SIZE            128         /* 任務堆棧大小 */
TaskHandle_t LEDTask_Handler;               /* 任務句柄 */
void led_task(void *pvParameters);          /* 任務函數 *//* KEY_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define KEY_TASK_PRIO           11          /* 任務優先級 */
#define KEY_STK_SIZE            128         /* 任務堆棧大小 */
TaskHandle_t KEYTask_Handler;               /* 任務句柄 */
void key_task(void *pvParameters);          /* 任務函數 *//* DISPLAY_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define DISPLAY_TASK_PRIO       12          /* 任務優先級 */
#define DISPLAY_STK_SIZE        512         /* 任務堆棧大小 */
TaskHandle_t DISPLAYTask_Handler;           /* 任務句柄 */
void display_task(void *pvParameters);      /* 任務函數 *//* 顯示消息隊列的數量 */
#define DISPLAYMSG_Q_NUM    20   /* 顯示消息隊列的數量 */
QueueHandle_t g_display_queue;     /* 顯示消息隊列句柄 *//******************************************************************************************************//*** @breif       加載UI* @param       mode :  bit0:0,不加載;1,加載前半部分UI*                      bit1:0,不加載;1,加載后半部分UI* @retval      無*/
void lwip_test_ui(uint8_t mode)
{uint8_t speed;uint8_t buf[30];if (mode & 1<< 0){lcd_show_string(6, 10, 200, 32, 32, "STM32", DARKBLUE);lcd_show_string(6, 40, lcddev.width, 24, 24, "lwIP UDP Test", DARKBLUE);lcd_show_string(6, 70, 200, 16, 16, "ATOM@ALIENTEK", DARKBLUE);}if (mode & 1 << 1){lcd_show_string(5, 110, 200, 16, 16, "lwIP Init Successed", MAGENTA);if (g_lwipdev.dhcpstatus == 2){sprintf((char*)buf,"DHCP IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);     /* 顯示動態IP地址 */}else{sprintf((char*)buf,"Static IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);    /* 打印靜態IP地址 */}lcd_show_string(5, 130, 200, 16, 16, (char*)buf, MAGENTA);speed = ethernet_chip_get_speed();      /* 得到網速 */if (speed){lcd_show_string(5, 150, 200, 16, 16, "Ethernet Speed:100M", MAGENTA);}else{lcd_show_string(5, 150, 200, 16, 16, "Ethernet Speed:10M", MAGENTA);}lcd_show_string(5, 170, 200, 16, 16, "KEY0:Send data", MAGENTA);lcd_show_string(5, 190, lcddev.width - 30, lcddev.height - 190, 16, "Receive Data:", BLUE); /* 提示消息 */}
}/*** @breif       freertos_demo* @param       無* @retval      無*/
void freertos_demo(void)
{/* start_task任務 */xTaskCreate((TaskFunction_t )start_task,(const char *   )"start_task",(uint16_t       )START_STK_SIZE,(void *         )NULL,(UBaseType_t    )START_TASK_PRIO,(TaskHandle_t * )&StartTask_Handler);vTaskStartScheduler(); /* 開啟任務調度 */
}/*** @brief       start_task* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void start_task(void *pvParameters)
{pvParameters = pvParameters;g_lwipdev.lwip_display_fn = lwip_test_ui;lwip_test_ui(1);    /* 加載后前部分UI */while (lwip_comm_init() != 0){lcd_show_string(30, 110, 200, 16, 16, "lwIP Init failed!!", RED);delay_ms(500);lcd_fill(30, 50, 200 + 30, 50 + 16, WHITE);lcd_show_string(30, 110, 200, 16, 16, "Retrying...       ", RED);delay_ms(500);LED1_TOGGLE();}while (!ethernet_read_phy(PHY_SR))  /* 檢查MCU與PHY芯片是否通信成功 */{printf("MCU與PHY芯片通信失敗,請檢查電路或者源碼!!!!\r\n");}while ((g_lwipdev.dhcpstatus != 2)&&(g_lwipdev.dhcpstatus != 0XFF))  /* 等待DHCP獲取成功/超時溢出 */{vTaskDelay(5);}taskENTER_CRITICAL();           /* 進入臨界區 */g_display_queue = xQueueCreate(DISPLAYMSG_Q_NUM,200);      /* 創建消息Message_Queue,隊列項長度是200長度 *//* 創建lwIP任務 */xTaskCreate((TaskFunction_t )lwip_demo_task,(const char*    )"lwip_demo_task",(uint16_t       )LWIP_DMEO_STK_SIZE, (void*          )NULL,(UBaseType_t    )LWIP_DMEO_TASK_PRIO,(TaskHandle_t*  )&LWIP_Task_Handler);/* key任務 */xTaskCreate((TaskFunction_t )key_task,(const char *   )"key_task",(uint16_t       )KEY_STK_SIZE,(void *         )NULL,(UBaseType_t    )KEY_TASK_PRIO,(TaskHandle_t * )&KEYTask_Handler);/* LED測試任務 */xTaskCreate((TaskFunction_t )led_task,(const char*    )"led_task",(uint16_t       )LED_STK_SIZE,(void*          )NULL,(UBaseType_t    )LED_TASK_PRIO,(TaskHandle_t*  )&LEDTask_Handler);/* 顯示任務 */xTaskCreate((TaskFunction_t )display_task,(const char*    )"display_task",(uint16_t       )DISPLAY_STK_SIZE,(void*          )NULL,(UBaseType_t    )DISPLAY_TASK_PRIO,(TaskHandle_t*  )&DISPLAYTask_Handler);vTaskDelete(StartTask_Handler); /* 刪除開始任務 */taskEXIT_CRITICAL();            /* 退出臨界區 */}/*** @brief       lwIP運行例程* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void lwip_demo_task(void *pvParameters)
{pvParameters = pvParameters;lwip_demo();            /* lwip測試代碼 */while (1){vTaskDelay(5);}
}/*** @brief       key_task* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void key_task(void *pvParameters)
{pvParameters = pvParameters;uint8_t key;while (1){key = key_scan(0);if (KEY0_PRES ==key){g_lwip_send_flag |= LWIP_SEND_DATA; /* 標記LWIP有數據要發送 */}vTaskDelay(10);}
}/*** @brief       系統再運行* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void led_task(void *pvParameters)
{pvParameters = pvParameters;while (1){LED1_TOGGLE();vTaskDelay(100);}
}/*** @brief       顯示任務* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void display_task(void *pvParameters)
{pvParameters = pvParameters;uint8_t *buffer;while (1){buffer = mymalloc(SRAMIN,200);if (g_display_queue != NULL){memset(buffer,0,200);       /* 清除緩沖區 */if (xQueueReceive(g_display_queue,buffer,portMAX_DELAY)){lcd_fill(30, 220, lcddev.width - 1, lcddev.height - 1, WHITE); /* 清上一次數據 *//* 顯示接收到的數據 */lcd_show_string(30, 220, lcddev.width - 30, lcddev.height - 230, 16, (char *)buffer, RED); }}myfree(SRAMIN,buffer);          /*釋放內存 */vTaskDelay(5);}
}

NETCONN 實現 TCP 客戶端
?

NETCONN 實現 TCP 客戶端連接步驟
?

NETCONN 實現 TCP 客戶端連接有以下幾步:

① 調用函數 netconn_new 創建 TCP 控制塊。

② 調用函數 netconn_connect 連接服務器。

③ 設置接收超時時間 tcp_clientconn->recv_timeout。

④ 調用函數 netconn_getaddr 獲取遠端 IP 地址和端口號。

⑤ 調用函數 netconn_write 和 netconn_recv 收發數據。

主要實現函數


/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define START_TASK_PRIO         5           /* 任務優先級 */
#define START_STK_SIZE          128         /* 任務堆棧大小 */
TaskHandle_t StartTask_Handler;             /* 任務句柄 */
void start_task(void *pvParameters);        /* 任務函數 *//* LWIP_DEMO 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define LWIP_DMEO_TASK_PRIO     11          /* 任務優先級 */
#define LWIP_DMEO_STK_SIZE      1024        /* 任務堆棧大小 */
TaskHandle_t LWIP_Task_Handler;             /* 任務句柄 */
void lwip_demo_task(void *pvParameters);    /* 任務函數 *//* LED_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define LED_TASK_PRIO           10          /* 任務優先級 */
#define LED_STK_SIZE            128         /* 任務堆棧大小 */
TaskHandle_t LEDTask_Handler;               /* 任務句柄 */
void led_task(void *pvParameters);          /* 任務函數 *//* KEY_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define KEY_TASK_PRIO           11          /* 任務優先級 */
#define KEY_STK_SIZE            128         /* 任務堆棧大小 */
TaskHandle_t KEYTask_Handler;               /* 任務句柄 */
void key_task(void *pvParameters);          /* 任務函數 *//* DISPLAY_TASK 任務 配置* 包括: 任務句柄 任務優先級 堆棧大小 創建任務*/
#define DISPLAY_TASK_PRIO       12          /* 任務優先級 */
#define DISPLAY_STK_SIZE        512         /* 任務堆棧大小 */
TaskHandle_t DISPLAYTask_Handler;           /* 任務句柄 */
void display_task(void *pvParameters);      /* 任務函數 *//* 顯示消息隊列的數量 */
#define DISPLAYMSG_Q_NUM    20   /* 顯示消息隊列的數量 */
QueueHandle_t g_display_queue;     /* 顯示消息隊列句柄 *//******************************************************************************************************//*** @breif       加載UI* @param       mode :  bit0:0,不加載;1,加載前半部分UI*                      bit1:0,不加載;1,加載后半部分UI* @retval      無*/
void lwip_test_ui(uint8_t mode)
{uint8_t speed;uint8_t buf[30];if (mode & 1<< 0){lcd_show_string(6, 10, 200, 32, 32, "STM32", DARKBLUE);lcd_show_string(6, 40, lcddev.width, 24, 24, "lwIP TCPClient Test", DARKBLUE);lcd_show_string(6, 70, 200, 16, 16, "ATOM@ALIENTEK", DARKBLUE);}if (mode & 1 << 1){lcd_show_string(5, 110, 200, 16, 16, "lwIP Init Successed", MAGENTA);if (g_lwipdev.dhcpstatus == 2){sprintf((char*)buf,"DHCP IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);     /* 顯示動態IP地址 */}else{sprintf((char*)buf,"Static IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);    /* 打印靜態IP地址 */}lcd_show_string(5, 130, 200, 16, 16, (char*)buf, MAGENTA);speed = ethernet_chip_get_speed();      /* 得到網速 */if (speed){lcd_show_string(5, 150, 200, 16, 16, "Ethernet Speed:100M", MAGENTA);}else{lcd_show_string(5, 150, 200, 16, 16, "Ethernet Speed:10M", MAGENTA);}lcd_show_string(5, 170, 200, 16, 16, "KEY0:Send data", MAGENTA);lcd_show_string(5, 190, lcddev.width - 30, lcddev.height - 190, 16, "Receive Data:", BLUE); /* 提示消息 */}
}/*** @breif       freertos_demo* @param       無* @retval      無*/
void freertos_demo(void)
{/* start_task任務 */xTaskCreate((TaskFunction_t )start_task,(const char *   )"start_task",(uint16_t       )START_STK_SIZE,(void *         )NULL,(UBaseType_t    )START_TASK_PRIO,(TaskHandle_t * )&StartTask_Handler);vTaskStartScheduler(); /* 開啟任務調度 */
}/*** @brief       start_task* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void start_task(void *pvParameters)
{pvParameters = pvParameters;g_lwipdev.lwip_display_fn = lwip_test_ui;lwip_test_ui(1);    /* 加載后前部分UI */while (lwip_comm_init() != 0){lcd_show_string(30, 110, 200, 16, 16, "lwIP Init failed!!", RED);delay_ms(500);lcd_fill(30, 50, 200 + 30, 50 + 16, WHITE);lcd_show_string(30, 110, 200, 16, 16, "Retrying...       ", RED);delay_ms(500);LED1_TOGGLE();}while (!ethernet_read_phy(PHY_SR))  /* 檢查MCU與PHY芯片是否通信成功 */{printf("MCU與PHY芯片通信失敗,請檢查電路或者源碼!!!!\r\n");}while ((g_lwipdev.dhcpstatus != 2)&&(g_lwipdev.dhcpstatus != 0XFF))  /* 等待DHCP獲取成功/超時溢出 */{vTaskDelay(5);}taskENTER_CRITICAL();           /* 進入臨界區 */g_display_queue = xQueueCreate(DISPLAYMSG_Q_NUM,200);      /* 創建消息Message_Queue,隊列項長度是200長度 *//* 創建lwIP任務 */xTaskCreate((TaskFunction_t )lwip_demo_task,(const char*    )"lwip_demo_task",(uint16_t       )LWIP_DMEO_STK_SIZE, (void*          )NULL,(UBaseType_t    )LWIP_DMEO_TASK_PRIO,(TaskHandle_t*  )&LWIP_Task_Handler);/* key任務 */xTaskCreate((TaskFunction_t )key_task,(const char *   )"key_task",(uint16_t       )KEY_STK_SIZE,(void *         )NULL,(UBaseType_t    )KEY_TASK_PRIO,(TaskHandle_t * )&KEYTask_Handler);/* LED測試任務 */xTaskCreate((TaskFunction_t )led_task,(const char*    )"led_task",(uint16_t       )LED_STK_SIZE,(void*          )NULL,(UBaseType_t    )LED_TASK_PRIO,(TaskHandle_t*  )&LEDTask_Handler);/* 顯示任務 */xTaskCreate((TaskFunction_t )display_task,(const char*    )"display_task",(uint16_t       )DISPLAY_STK_SIZE,(void*          )NULL,(UBaseType_t    )DISPLAY_TASK_PRIO,(TaskHandle_t*  )&DISPLAYTask_Handler);vTaskDelete(StartTask_Handler); /* 刪除開始任務 */taskEXIT_CRITICAL();            /* 退出臨界區 */}/*** @brief       lwIP運行例程* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void lwip_demo_task(void *pvParameters)
{pvParameters = pvParameters;lwip_demo();            /* lwip測試代碼 */while (1){vTaskDelay(5);}
}/*** @brief       key_task* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void key_task(void *pvParameters)
{pvParameters = pvParameters;uint8_t key;while (1){key = key_scan(0);if (KEY0_PRES ==key){g_lwip_send_flag |= LWIP_SEND_DATA; /* 標記LWIP有數據要發送 */}vTaskDelay(10);}
}/*** @brief       系統再運行* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void led_task(void *pvParameters)
{pvParameters = pvParameters;while (1){LED1_TOGGLE();vTaskDelay(100);}
}/*** @brief       顯示任務* @param       pvParameters : 傳入參數(未用到)* @retval      無*/
void display_task(void *pvParameters)
{pvParameters = pvParameters;uint8_t *buffer;while (1){buffer = mymalloc(SRAMIN,200);if (g_display_queue != NULL){memset(buffer,0,200);       /* 清除緩沖區 */if (xQueueReceive(g_display_queue,buffer,portMAX_DELAY)){lcd_fill(30, 220, lcddev.width - 1, lcddev.height - 1, WHITE); /* 清上一次數據 *//* 顯示接收到的數據 */lcd_show_string(30, 220, lcddev.width - 30, lcddev.height - 230, 16, (char *)buffer, RED); }}myfree(SRAMIN,buffer);          /*釋放內存 */vTaskDelay(5);}
}


/* 這個必須填寫正確,遠程IP地址 */
#define DEST_IP_ADDR0               192
#define DEST_IP_ADDR1               168
#define DEST_IP_ADDR2                 1
#define DEST_IP_ADDR3               111#define LWIP_DEMO_RX_BUFSIZE         2000  /* 最大接收數據長度 */
#define LWIP_DEMO_PORT               8080  /* 連接的本地端口號 *//* 接收數據緩沖區 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 
/* 發送數據內容 */
char *g_lwip_demo_sendbuf = "ALIENTEK DATA\r\n";
/* 數據發送標志位 */
uint8_t g_lwip_send_flag;
extern QueueHandle_t g_display_queue;     /* 顯示消息隊列句柄 *//*** @brief       lwip_demo實驗入口* @param       無* @retval      無*/
void lwip_demo(void)
{static struct netconn *tcp_clientconn = NULL; /* TCP CLIENT網絡連接結構體 */uint32_t data_len = 0;struct pbuf *q;err_t err,recv_err;ip4_addr_t server_ipaddr,loca_ipaddr;static uint16_t server_port,loca_port;BaseType_t lwip_err;char *tbuf;server_port = LWIP_DEMO_PORT;IP4_ADDR(&server_ipaddr,DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3);   /* 構造目的IP地址 */tbuf = mymalloc(SRAMIN, 200); /* 申請內存 */sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客戶端端口號 */lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);while (1) {tcp_clientconn = netconn_new(NETCONN_TCP);                                      /* 創建一個TCP鏈接 */err = netconn_connect(tcp_clientconn,&server_ipaddr,server_port);               /* 連接服務器 */if (err != ERR_OK){printf("接連失敗\r\n");netconn_delete(tcp_clientconn);                                             /* 返回值不等于ERR_OK,刪除tcp_clientconn連接 */}else if (err == ERR_OK)                                                         /* 處理新連接的數據 */{ struct netbuf *recvbuf;tcp_clientconn->recv_timeout = 10;netconn_getaddr(tcp_clientconn,&loca_ipaddr,&loca_port,1);                  /* 獲取本地IP主機IP地址和端口號 */printf("連接上服務器%d.%d.%d.%d,本機端口號為:%d\r\n",DEST_IP_ADDR0,DEST_IP_ADDR1, DEST_IP_ADDR2,DEST_IP_ADDR3,loca_port);lcd_show_string(5, 90, 200, 16, 16, "State:Connection Successful", BLUE);while (1){if ((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA)                 /* 有數據要發送 */{err = netconn_write(tcp_clientconn ,g_lwip_demo_sendbuf,strlen((char*)g_lwip_demo_sendbuf),NETCONN_COPY); /* 發送tcp_server_sentbuf中的數據 */if (err != ERR_OK){printf("發送失敗\r\n");}g_lwip_send_flag &= ~LWIP_SEND_DATA;}if ((recv_err = netconn_recv(tcp_clientconn,&recvbuf)) == ERR_OK)            /* 接收到數據 */{taskENTER_CRITICAL();                                                    /* 進入臨界區 */memset(g_lwip_demo_recvbuf,0,LWIP_DEMO_RX_BUFSIZE);                      /* 數據接收緩沖區清零 */for (q = recvbuf->p;q != NULL;q = q->next)                               /* 遍歷完整個pbuf鏈表 */{/* 判斷要拷貝到TCP_CLIENT_RX_BUFSIZE中的數據是否大于TCP_CLIENT_RX_BUFSIZE的剩余空間,如果大于 *//* 的話就只拷貝TCP_CLIENT_RX_BUFSIZE中剩余長度的數據,否則的話就拷貝所有的數據 */if (q->len > (LWIP_DEMO_RX_BUFSIZE - data_len)) {memcpy(g_lwip_demo_recvbuf + data_len,q->payload,(LWIP_DEMO_RX_BUFSIZE - data_len));/* 拷貝數據 */}else {memcpy(g_lwip_demo_recvbuf + data_len,q->payload,q->len);}data_len += q->len;if (data_len > LWIP_DEMO_RX_BUFSIZE) {break;                  /* 超出TCP客戶端接收數組,跳出 */}}taskEXIT_CRITICAL();            /* 退出臨界區 */data_len = 0;                   /* 復制完成后data_len要清零 */lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);if (lwip_err == errQUEUE_FULL){printf("隊列Key_Queue已滿,數據發送失敗!\r\n");}netbuf_delete(recvbuf);}else if (recv_err == ERR_CLSD)       /* 關閉連接 */{netconn_close(tcp_clientconn);netconn_delete(tcp_clientconn);printf("服務器%d.%d.%d.%d斷開連接\r\n",DEST_IP_ADDR0,DEST_IP_ADDR1, DEST_IP_ADDR2,DEST_IP_ADDR3);lcd_fill(5, 89, lcddev.width,110, WHITE);lcd_show_string(5, 90, 200, 16, 16, "State:Disconnect", BLUE);myfree(SRAMIN, tbuf);break;}}}}
}

NETCONN 實現 TCP 服務器端

NETCONN 實現 TCP 服務器步驟
?

NETCONN 實現 TCP 服務器有以下幾步:

① 調用函數 netconn_new 創建 TCP 控制塊。

② 調用函數 netconn_bind 綁定 TCP 控制塊、本地 IP 地址和端口號。

③ 調用函數 netconn_listen 進入監聽模式。

④ 設置接收超時時間 conn->recv_timeout。

⑤ 調用函數 netconn_accept 接收連接請求。

⑥ 調用函數 netconn_getaddr 獲取遠端 IP 地址和端口號。

⑦ 調用函數 netconn_write 和 netconn_recv 收發數據。

正常開發,要分開接收和發送任務,且不能使用阻塞

主要實現代碼


/* TCPServer 不需要設置遠程IP地址 */
//#define DEST_IP_ADDR0               192
//#define DEST_IP_ADDR1               168
//#define DEST_IP_ADDR2                 1
//#define DEST_IP_ADDR3               167#define LWIP_DEMO_RX_BUFSIZE         2000  /* 最大接收數據長度 */
#define LWIP_DEMO_PORT               8080  /* 連接的本地端口號 *//* 接收數據緩沖區 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 
/* 發送數據內容 */
char *g_lwip_demo_sendbuf = "ALIENTEK DATA \r\n";
/* 數據發送標志位 */
uint8_t g_lwip_send_flag;extern QueueHandle_t g_display_queue;     /* 顯示消息隊列句柄 *//*** @brief       lwip_demo實驗入口* @param       無* @retval      無*/
void lwip_demo(void)
{static struct netconn *tcp_serverconn = NULL; /* TCP SERVER網絡連接結構體 */uint32_t  data_len = 0;struct    pbuf *q;err_t     err,recv_err;uint8_t   remot_addr[4];struct netconn *newconn;static    ip_addr_t ipaddr;static    u16_t  port;BaseType_t lwip_err;char *tbuf;tbuf = mymalloc(SRAMIN, 200); /* 申請內存 */sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客戶端端口號 */lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);/* 第一步:創建一個TCP控制塊 */tcp_serverconn = netconn_new(NETCONN_TCP);                      /* 創建一個TCP鏈接 *//* 第二步:綁定TCP控制塊、本地IP地址和端口號 */netconn_bind(tcp_serverconn,IP_ADDR_ANY,LWIP_DEMO_PORT);        /* 綁定端口 8080號端口 *//* 第三步:監聽 */netconn_listen(tcp_serverconn);                                 /* 進入監聽模式 */tcp_serverconn->recv_timeout = 10;                              /* 禁止阻塞線程 等待10ms */while (1) {/* 第四步:接收連接請求 */err = netconn_accept(tcp_serverconn,&newconn);              /* 接收連接請求 */if (err == ERR_OK) newconn->recv_timeout = 10;if (err == ERR_OK)                                          /* 處理新連接的數據 */{ struct netbuf *recvbuf;netconn_getaddr(newconn,&ipaddr,&port,0);               /* 獲取遠端IP地址和端口號 */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);lcd_show_string(5, 90, 200, 16, 16, "State:Connection Successful", BLUE);while (1){if ((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) /* 有數據要發送 */{err = netconn_write(newconn ,g_lwip_demo_sendbuf,strlen((char*)g_lwip_demo_sendbuf),NETCONN_COPY); /* 發送g_lwip_demo_sendbuf中的數據 */if(err != ERR_OK){printf("發送失敗\r\n");}g_lwip_send_flag &= ~LWIP_SEND_DATA;}if ((recv_err = netconn_recv(newconn,&recvbuf)) == ERR_OK)           /* 接收到數據 */{ taskENTER_CRITICAL();                                           /* 進入臨界區 */memset(g_lwip_demo_recvbuf,0,LWIP_DEMO_RX_BUFSIZE);               /* 數據接收緩沖區清零 */for (q = recvbuf->p;q != NULL;q = q->next)                       /* 遍歷完整個pbuf鏈表 */{/* 判斷要拷貝到LWIP_DEMO_RX_BUFSIZE中的數據是否大于LWIP_DEMO_RX_BUFSIZE的剩余空間,如果大于 *//* 的話就只拷貝LWIP_DEMO_RX_BUFSIZE中剩余長度的數據,否則的話就拷貝所有的數據 */if(q->len > (LWIP_DEMO_RX_BUFSIZE-data_len)){memcpy(g_lwip_demo_recvbuf + data_len,q->payload,(LWIP_DEMO_RX_BUFSIZE - data_len));/* 拷貝數據 */}else{memcpy(g_lwip_demo_recvbuf + data_len,q->payload,q->len);}data_len += q->len;if(data_len > LWIP_DEMO_RX_BUFSIZE){break;   /*超出TCP客戶端接收數組,跳出*/}}taskEXIT_CRITICAL();                                /* 退出臨界區 */data_len = 0;                                       /* 復制完成后data_len要清零 */lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);if (lwip_err == errQUEUE_FULL){printf("隊列Key_Queue已滿,數據發送失敗!\r\n");}netbuf_delete(recvbuf);}else if (recv_err == ERR_CLSD)                           /* 關閉連接 */{netconn_close(newconn);netconn_delete(newconn);printf("主機:%d.%d.%d.%d斷開與服務器的連接\r\n",remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3]);lcd_fill(5, 89, lcddev.width,110, WHITE);lcd_show_string(5, 90, 200, 16, 16, "State:Disconnect", BLUE);myfree(SRAMIN, tbuf);break;//跳出whiel  再次連接任務}}}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/906097.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/906097.shtml
英文地址,請注明出處:http://en.pswp.cn/news/906097.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux搜索

假如我們要搜索 struct sockaddr_in 我們在命令終端輸入 cd/usr/include/ //進入頭文件目錄地址 /usr/include/ grep " struct sockaddr_in { " *-nir &#xff08;*是在當前目錄&#xff0c;n 是找出來顯示行數…

2025長三角杯數學建模B題思路模型代碼:空氣源熱泵供暖的溫度預測,賽題分析與思路

2025長三角杯數學建模B題思路模型代碼&#xff0c;詳細內容見文末名片 空氣源熱泵是一種與中央空調類似的設備&#xff0c;其結構主要由壓縮主機、熱交換 器以及末端構成&#xff0c;依靠水泵對末端房屋提供熱量來實現制熱。空氣源熱泵作為熱 慣性負載&#xff0c;調節潛力巨…

ssh免密碼登錄

創建秘鑰和公鑰 ssh-keygen -t rsa 輸入上述命令后&#xff0c;直接按回車即可&#xff0c;完成后會在上面信息顯示&#xff0c;生成的文件路徑信息 id_rsa&#xff1a;秘鑰 id_rsa.pub&#xff1a; 公鑰 將公鑰的內容copy到遠端 將id_rsa.pub的內容拷貝到~/.ssh下的authori…

基于Bootstrap 的網頁html css 登錄頁制作成品

目錄 前言 一、網頁制作概述 二、登錄頁面 2.1 HTML內容 2.2 CSS樣式 三、技術說明書 四、頁面效果圖 前言 ?Bootstrap?是一個用于快速開發Web應用程序和網站的前端框架&#xff0c;由Twitter的設計師Mark Otto和Jacob Thornton合作開發。 它基于HTML、CSS和JavaScri…

20倍云臺球機是一種高性能的監控設備

20倍云臺球機是一種高性能的監控設備&#xff0c;其主要特點包括20倍光學變焦能力和云臺旋轉功能。以下是對20倍云臺球機的詳細分析&#xff1a; 一、主要特點 20倍光學變焦 &#xff1a; 攝像機鏡頭能夠在保持圖像清晰度的前提下&#xff0c;將監控目標放大20倍。 這一功能…

大型語言模型應用十大安全風險

40多頁LLM應用的十大風險 這是一份關于LLM應用的十大風險&#xff08;2025版&#xff09;&#xff0c;有一定的參考價值。 如果你時間充裕&#xff0c;可以聽聽播客&#xff0c;詳細了解&#xff1a; 如果你只想快速了解10條分別是什么&#xff0c;可以直接看重點摘錄&#xff…

一文掌握工業相機選型計算

目錄 一、基本概念 1.1 物方和像方 1.2 工作距離和視場 1.3 放大倍率 1.4 相機芯片尺寸 二、公式計算 三、實例應用 一、基本概念 1.1 物方和像方 在光學領域&#xff0c;物方&#xff08;Object Space&#xff09;是與像方&#xff08;Image Space&#xff09;相對的…

《虛擬即真實:數字人驅動技術在React Native社交中的涅槃》

當React Native與數字人驅動技術相遇&#xff0c;它們將如何攜手塑造社交應用中智能客服與虛擬主播的自然交互呢&#xff1f;這正是本文要深入探討的話題。 React Native是Facebook開源的一個用于構建原生移動應用的框架&#xff0c;它允許開發者使用JavaScript和React編寫代碼…

使用AI 生成PPT 最佳實踐方案對比

文章大綱 一、專業AI生成工具(推薦新手)**1. 推薦工具詳解****2. 操作流程優化****3. 優勢與局限**二、代碼生成方案(開發者推薦)**1. Python-pptx進階用法****2. GitHub推薦**三、混合工作流(平衡效率與定制)**1. 工具鏈升級****2. 示例Markdown結構**四、網頁轉換方案(…

前端-HTML元素

目錄 HTML標簽是什么&#xff1f; 什么是HTML元素&#xff1f; HTML元素有哪些分類方法&#xff1f; 什么是HTML頭部元素 更換路徑 注&#xff1a;本文以leetbook為基礎 HTML標簽是什么&#xff1f; HTML標簽是HTML語言中最基本單位和重要組成部分 雖然它不區分大小寫&a…

菱形繼承原理

在C中&#xff0c;菱形繼承的內存模型會因是否使用虛繼承產生本質差異。我們通過具體示例說明兩種場景的區別&#xff1a; 一、普通菱形繼承的內存模型 class A { int a; }; class B : public A { int b; }; class C : public A { int c; }; class D : public B, public C { i…

2025認證杯數學建模第二階段A題小行星軌跡預測思路+模型+代碼

2025認證杯數學建模第二階段思路模型代碼&#xff0c;詳細內容見文末名片 一、問題重述 1.1 問題背景 在浩瀚無垠的宇宙中&#xff0c;近地小行星&#xff08;NEAs&#xff09;宛如一顆顆神秘的“太空子彈”&#xff0c;其軌道相對接近地球&#xff0c;給我們的藍色星球帶來…

掌握Docker Commit:輕松創建自定義鏡像

使用 docker commit 命令可以通過對現有容器進行修改來創建新的鏡像。-a 選項用于指定作者信息&#xff0c;-m 選項用于添加提交信息。以下是具體步驟&#xff1a; 啟動并修改容器 啟動一個容器并進行必要的修改。例如&#xff0c;啟動一個 Ubuntu 容器并安裝一些軟件包&…

Java虛擬機 - JVM與Java體系結構

Java虛擬機 JVM與Java體系結構為什么要學習JVMJava與JVM簡介Java 語言的核心特性JVM&#xff1a;Java 生態的基石JVM的架構模型基于棧的指令集架構&#xff08;Stack-Based&#xff09;基于寄存器的指令集架構&#xff08;Register-Based&#xff09;JVM生命周期 總結 JVM與Jav…

【PostgreSQL系列】PostgreSQL 復制參數詳解

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

阿里巴巴開源移動端多模態LLM工具——MNN

MNN 是一個高效且輕量級的深度學習框架。它支持深度學習模型的推理和訓練&#xff0c;并在設備端的推理和訓練方面具有行業領先的性能。目前&#xff0c;MNN 已集成到阿里巴巴集團的 30 多個應用中&#xff0c;如淘寶、天貓、優酷、釘釘、閑魚等&#xff0c;覆蓋了直播、短視頻…

Vue.js---watch 的實現原理

4.7 watch 的實現原理 watch本質上就是使用了effect以及options.scheduler 定義watch函數&#xff1a; // watch函數:傳入參數source以及回調函數function watch(source , cb) {effect(() > source.foo,{scheduler(){// 回調函數cb()}})}watch接收兩個參數分別是source和c…

SpringBoot3+AI

玩一下AI 1. SSE協議 我們都知道tcp&#xff0c;ip&#xff0c;http&#xff0c;https&#xff0c;websocket等等協議&#xff0c;今天了解一個新的協議SSE協議&#xff08;Server-Sent Events&#xff09; SSE&#xff08;Server-Sent Events&#xff09; 是一種允許服務器…

vscode中Debug c++

在vscode中Debug ros c程序 1 在Debug模式下編譯 如果用命令行catkin_make&#xff0c;在輸入catkin_make時加上一個參數&#xff1a; catkin_make -DCMAKE_BUILD_TYPEDebug 或者直接修改CMakelist.txt&#xff0c;添加以下代碼&#xff1a; SET(CMAKE_BUILD_TYPE "D…

【ROS2】 核心概念6——通信接口語法(Interfaces)

古月21講/2.6_通信接口 官方文檔&#xff1a;Interfaces — ROS 2 Documentation: Humble documentation 官方接口代碼實戰&#xff1a;https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Single-Package-Define-And-Use-Interface.html ROS 2使用簡化的描…