LWIP的NETCONN接口

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的邮箱

存在发送邮箱,那么就有接收邮箱

此时产生新的流程,得到数据如下;(如下:前三个,类似于初始化位置)

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 邮箱中接收数据包(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  再次连接任务
                }
            }
        }
    }
}
相关推荐
Yawesh_best9 小时前
告别系统壁垒!WSL+cpolar 让跨平台开发效率翻倍
运维·服务器·数据库·笔记·web安全
Ccjf酷儿11 小时前
操作系统 蒋炎岩 3.硬件视角的操作系统
笔记
习习.y12 小时前
python笔记梳理以及一些题目整理
开发语言·笔记·python
在逃热干面12 小时前
(笔记)自定义 systemd 服务
笔记
DKPT14 小时前
ZGC和G1收集器相比哪个更好?
java·jvm·笔记·学习·spring
QT 小鲜肉15 小时前
【孙子兵法之上篇】001. 孙子兵法·计篇
笔记·读书·孙子兵法
星轨初途16 小时前
数据结构排序算法详解(5)——非比较函数:计数排序(鸽巢原理)及排序算法复杂度和稳定性分析
c语言·开发语言·数据结构·经验分享·笔记·算法·排序算法
QT 小鲜肉16 小时前
【孙子兵法之上篇】001. 孙子兵法·计篇深度解析与现代应用
笔记·读书·孙子兵法
love530love19 小时前
【笔记】ComfUI RIFEInterpolation 节点缺失问题(cupy CUDA 安装)解决方案
人工智能·windows·笔记·python·插件·comfyui
愚戏师19 小时前
MySQL 数据导出
数据库·笔记·mysql