STM32F407VET6 基于FreeRTOS实时操作系统和LwIP协议栈创建TCP客户端

  在上一篇博客中我们移植好了FreeRTOS + LwIP + LAN8720网卡,现在我们在上一篇博客的工程基础上创建一个TCP客户端进行网络通信。

  注:如果要自动获取本地IP地址,那就要使能DHCP功能,在lwipopts.h文件配置。这里我不使用DHCP功能,而是使用静态IP。

1、工程部分

  首先创建两个文件,分别为tcp_client.c、tcp_client.h,然后保存在LwIP\app目录下,然后在工程中添加tcp_client.c文件。如下图所示:

  

 

 

2、代码部分

 

tcp_client.h

#ifndef __TCP_CLIENT_H
#define __TCP_CLIENT_H
 

/********************************************************
 * 函数功能:创建TCP客户端
 * 形    参:ip_msg:IP信息数据结构指针
 * 返 回 值:0=成功
             1=TCP客户端线程创建失败
             2=TCP客户端数据列队创建失败
 ********************************************************/
unsigned int tcp_client_init(void *ip_msg);

/********************************************************
 * 函数功能:TCP客户端重连
 * 形    参:无
 * 返 回 值:0=成功
             1=失败
 ********************************************************/
unsigned int tcp_client_reconnect(void);

/********************************************************
 * 函数功能:获取TCP客户端连接状态
 * 形    参:无
 * 返 回 值:0=连接正常
             1=连接异常
 ********************************************************/
unsigned int tcp_client_connect_status_get(void);

/********************************************************
 * 函数功能:TCP客户端向网口发送数据
 * 形    参:pbuf:数据缓存地址
             length:发送数据字节数
 * 返 回 值:0=成功
             1=数据缓存地址为NULL
             2=数据长度错误
             3=客户端未启动
             4=连接异常
 ********************************************************/
unsigned int tcp_client_write(const unsigned char *pbuf, unsigned int length);

/********************************************************
 * 函数功能:获取TCP客户端数据列队句柄
 * 形    参:无
 * 返 回 值:数据列队句柄
 ********************************************************/
void *tcp_client_queue(void);
 
 
#endif
 tcp_clent.c

 

#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "sys_eth.h"
#include "lwip/api.h"
#include "tcp_client.h"

// TCP客户端接收数据结构
#pragma pack(push, 1)
typedef struct _sTcpClientRxMsg
{
   unsigned char *pbuf;
   unsigned int length;
}sTcpClientRxMsg_t;
#pragma pack(pop)

static struct netconn *socket = NULL; // TCP客户端套接字
static xQueueHandle TcpClientQueue = NULL; // 接收数据列队
static unsigned char connect_flag = pdFALSE; // TCP客户端连接标志
 

/********************************************************
 * 函数功能:TCP客户端数据接收线程
 * 形    参:arg:线程形参
 * 返 回 值:无
 ********************************************************/
static void tcp_client_thread(void *parg)
{
   sLwipDev_t *psLwipDev = (sLwipDev_t *)parg;
 
   while(psLwipDev != NULL)
   {
      // 创建一个套接字
      socket = netconn_new(NETCONN_TCP);
      if(socket == NULL)
      {
         vTaskDelay(100);
         continue; // TCP连接创建失败则从新创建
      }
  
      // 绑定IP端口,并连接服务器
      ip_addr_t LocalIP = {0};
      ip_addr_t RemoteIP = {0};
      IP4_ADDR(&LocalIP, psLwipDev->localip[0], psLwipDev->localip[1], psLwipDev->localip[2], psLwipDev->localip[3]);
      IP4_ADDR(&RemoteIP, psLwipDev->remoteip[0], psLwipDev->remoteip[1], psLwipDev->remoteip[2], psLwipDev->remoteip[3]);
      netconn_bind(socket, &LocalIP, psLwipDev->localport); // 绑定本地端口和IP,绑定后使用此IP和端口对外发送
      if(netconn_connect(socket, &RemoteIP, psLwipDev->remoteport) != ERR_OK) // 连接服务器
      {
         netconn_delete(socket); // 服务器连接失败,删除连接
         vTaskDelay(100);
         continue;
      }
      err_t recv_err;
      struct pbuf *q;
      struct netbuf *recvbuf;
      socket->recv_timeout = 10;
      connect_flag = pdTRUE; // 连接标志置位
      while(connect_flag == pdTRUE)
      {
         // 接收数据(注:实测每次接收的数据量最大为1460个字节)
         recv_err = netconn_recv(socket, &recvbuf);
         if(recv_err == ERR_OK) // 接收到数据
         {
            unsigned int length = 0;
            sTcpClientRxMsg_t msg = {0};
    
            taskENTER_CRITICAL(); // 进入临界区
            {
               length = recvbuf->p->tot_len;
               msg.pbuf = pvPortMalloc(length);
               if(msg.pbuf != NULL)
               {
                  for(q = recvbuf->p; q != NULL; q = q->next)
                  {
                     if((msg.length + q->len) > length)
                     {
                        break; // 缓存不够,直接退出
                     }
       
                     unsigned char *pload = (unsigned char *)q->payload;
                     for(unsigned int i = 0; i < q->len; i++)
                     {
                        msg.pbuf[msg.length++] = pload[i];
                     }
                  }
      
                  netbuf_delete(recvbuf);
               }
            }
            taskEXIT_CRITICAL(); // 退出临界区
            xQueueSend(TcpClientQueue, &msg, 0);
         }
         else if(recv_err == ERR_CLSD) // 服务器主动关闭连接
         {
            tcp_client_reconnect();
         }
      }
  
      // 连接异常
      netconn_close(socket); // 关闭套接字
      netconn_delete(socket); // 释放资源
   }
}

/********************************************************
 * 函数功能:创建TCP客户端
 * 形    参:ip_msg:IP信息数据结构指针
 * 返 回 值:0=成功
             1=TCP客户端线程创建失败
             2=TCP客户端数据列队创建失败
 ********************************************************/
unsigned int tcp_client_init(void *ip_msg)
{
   vPortFree(TcpClientQueue);
   TcpClientQueue = xQueueCreate(50, sizeof(sTcpClientRxMsg_t));
   if(TcpClientQueue == NULL)
   {
      return 2;
   }
 
   // 创建任务,参数:任务函数、任务名称、任务堆栈大小、任务函数形参、任务优先级、任务句柄
   if(xTaskCreate(tcp_client_thread, "tcp_client_thread", 256, ip_msg, 5, NULL) != pdPASS)
   {
      return 1; // 创建失败
   }
   return 0;
}

/********************************************************
 * 函数功能:获取TCP客户端数据列队句柄
 * 形    参:无
 * 返 回 值:数据列队句柄
 ********************************************************/
void *tcp_client_queue(void)
{
   return TcpClientQueue;
}

/********************************************************
 * 函数功能:TCP客户端重连
 * 形    参:无
 * 返 回 值:0=成功
             1=失败
 ********************************************************/
unsigned int tcp_client_reconnect(void)
{
   connect_flag = pdFALSE; // 清除连接标志
   if(connect_flag == pdFALSE)
   {
      return 0;
   }
   else
   {
      return 1;
   }
}

/********************************************************
 * 函数功能:获取TCP客户端连接状态
 * 形    参:无
 * 返 回 值:0=连接正常
             1=连接异常
 ********************************************************/
unsigned int tcp_client_connect_status_get(void)
{
   if(connect_flag == pdFALSE)
   {
      return 1;
   }
   else
   {
      return 0;
   }
}

/********************************************************
 * 函数功能:TCP客户端向网口发送数据
 * 形    参:pbuf:数据缓存地址
             length:发送数据字节数
 * 返 回 值:0=成功
             1=数据缓存地址为NULL
             2=数据长度错误
             3=客户端未启动
             4=连接异常
 ********************************************************/
unsigned int tcp_client_write(const unsigned char *pbuf, unsigned int length)
{
   unsigned char retry = 5;
 
   if(pbuf == NULL)
   {
      return 1;
   }
 
   if(length == 0)
   {
      return 2;
   }
 
   if(connect_flag == pdFALSE)
   {
      return 3;
   }
 
   while(netconn_write(socket, pbuf, length, NETCONN_COPY) != ERR_OK) // 发送数据
   {
      vTaskDelay(100);
      if(--retry == 0)
      {
         tcp_client_reconnect();
         return 4;
      }
   }
 
   return 0; // 发送成功
}
 
main.c
 
#include "stm32f4xx.h"
#include "delay.h"
#include "led.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "sys_eth.h"
#include "tcp_client.h"
// TCP客户端接收数据结构
#pragma pack(push, 1)
typedef struct _sTcpClientRxMsg
{
   unsigned char *pbuf;
   unsigned int length;
}sTcpClientRxMsg_t;
#pragma pack(pop)

static void tcp_client(void *parg)
{
   sLwipDev_t sLwipDev = {0};
   sTcpClientRxMsg_t msg = {0};
 
   // 以太网、LwIP协议栈初始化
   eth_default_ip_get(&sLwipDev);
   while(eth_init(&sLwipDev) != 0)
   {
      vTaskDelay(500);
   }
 
   tcp_client_init(&sLwipDev);
 
   while(1)
   {
      if(xQueueReceive(tcp_client_queue(), &msg, portMAX_DELAY) == pdPASS)
      {
         tcp_client_write(msg.pbuf, msg.length);
       vPortFree(msg.pbuf);
      }
   }
}

int main(void)
{
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
   systick_init();
 
   // 参数:任务函数指针、任务名称、任务栈大小、任务函数形参、任务优先级、任务句柄
   xTaskCreate(tcp_client, "tcp_client", 512, NULL, 3, NULL); // 创建TCP客户端任务
 
   // 启动任务调度器
   vTaskStartScheduler();
   return 1; // 运行到此处表示系统异常
}
 
3、运行结果 
  

 

   可以看到运行结果正常。

 

  至此TCP客户端创建完成。

 

 

posted @ 2020-05-21 10:42  一梦一人生  阅读(2957)  评论(0编辑  收藏  举报