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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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使用简化的描…