NETCONN****接口简介
NETCONN API 使用了操作系统的 IPC 机制, 对网络连接进行了抽象,使用同一的接口完成UDP和TCP连接
NETCONN API接口是在RAW接口基础上延申出来的一套API****接口

NETCONN****实现原理
2.1**,NETCONN控制块**
2.2**,NETCONN收发的数据管理**
2.3**,NETCONN实现原理**
NETCONN****控制块
cpp
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****收发的数据管理
cpp
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的邮箱
存在发送邮箱,那么就有接收邮箱
此时产生新的流程,得到数据如下;(如下:前三个,类似于初始化位置)

NETCONN与RAW****接口的区别
|--------|-----------------|-------------|
| 区别 | 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 邮箱中接收数据包(UDP和TCP可用) |
| 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
cpp
/* 这个必须填写正确,远程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
cpp
/******************************************************************************************************/
/*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 收发数据。
主要实现函数
cpp
/******************************************************************************************************/
/*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);
}
}
cpp
/* 这个必须填写正确,远程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 收发数据。
正常开发,要分开接收和发送任务,且不能使用阻塞

主要实现代码
cpp
/* 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 再次连接任务
}
}
}
}
}