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


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

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

    1、工程部分

      首先创建两个文件,分别为tcp_client.c、tcp_client.h,然后保存在LwIPapp目录下,然后在工程中添加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客户端创建完成。

  • 相关阅读:
    提高你的Java代码质量吧:使用构造函数协助描述枚举项
    Python文件或目录操作的常用函数
    汉语-词语:胸怀
    汉语-词语:胸襟
    汉语-词语:谋略
    汉语-词语:气量
    植物-常见树木:刺槐
    植物-常见树木:楝
    HDU 2255 奔小康赚大钱 KM算法题解
    Dozer--第三方复制工具,哎哟,还不错!
  • 原文地址:https://www.cnblogs.com/icode-wzc/p/12929136.html
Copyright © 2020-2023  润新知