stm32 lwip client同时稳定收发和自动重连

告别‘上电必须插网线’:STM32F407+LWIP实战,教你实现网线热插拔与断线重连 本文详细介绍了如何基于STM32F407和LWIP协议栈实现工业级网络热插拔与断线自愈功能。通过深度优化PHY芯片状态检测、重构LWIP网络接口管理,并设计智能状态机,解决了传统方案中网线热插拔导致的通信中断问题。文章重点解析了连接优化策略,包括防抖动设计、安全重启netif接口及资源竞争处理,为工业自动化和物联网设备提供稳定可靠的网络连接方案。 阅读详情

1 背景

Lwip是适用于类似嵌入式等资源有限的系统,要实现lwip在嵌入式系统上的开发遍历和功能迭代,一般需要搭配rtos一起实现,可以避免繁杂的事件回调,轻松实现任务分离和提高对上层协议的适配度。

在使用lwip开发tcp client过程中,一个比较常见的功能就是:要求client周期性的向server请求数据,要求client实时接收server的数据。但是在过程中发现client的发送部分,并不能自动检测到与server的连接状态,导致无法在server主动断开连接后做出重连动作。

2 Socket收发流程

在LwIP中,使用Socket接口(也叫 BSD Socket API)来开发TCP Client是最方便、也最接近PC编程习惯的方式。

作为TCP client收发数据的核心流程是:建立Socket → 连接Server → 收发数据

2.1 建立socket

使用socket创建一个TCP类型的socket;

int sock = -1;

sock = socket (AF_INET, SOCK_STREAM, 0) :

if (sock < 0)
{
      printf("Socket error\n");      //创建失败的处理
      vTaskDelay(10);
      continue;
}

函数原型:int lwip_socket(int domain, int type, int protocol);

socket()第一个参数domain:其中AF_INET表示IPv4协议族,在lwip中一般都是AF_INET

socket()第二个参数type:SOCK_STREAM表示套接字类型为流套接字,对应于TCP协议;SOCK_DGRAM表示数据报套接字,对应于DUP协议;SOCK_RAW表示原始套接字,能够处理IP层协议。

socket()第三个参数protocol:通常为0,让系统根据type自动选择。

返回:分配socket成功返回socket编号,否则为 -1。

2.2 连接Server

配置好服务器的IP地址和端口号,然后调用 connect() 函数连接server。

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons((DEST_PORT);  // 端口号,需要转换为网络字节序
server_addr.sin_addr.s_addr = ipaddr.addr; // 服务器的IP地址

if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
    // 连接失败处理
    printf("Connect failed!\r\n");
}

函数原型:int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen);

connect ()第一个参数s:socket编号,对应于socket()返回的值;

connect ()第二个参数name:包含server的IP和port,以及连接server的协议族为ipv4。

socket()第三个参数namelen:是sockaddr_in的大小。

返回:连接server成功返回0,否则为 -1。

2.3 发送数据

在连接成功后,在一个循环中按需求发送数据包,如果client本地socket发送失败,则退出循环重新连接server。

while (1)
{
      if(write(sock,send_buf,sizeof(send_buf)) < 0)
      break;
      vTaskDelay(1000);
}

2..4 接收数据

在连接成功后,在一个循环中阻塞接收数据包,如果client接收出错,则退出循环重新连接server。

while (1)
{
    ulreturn = xSemaphoreTake(Binary_Sem, 0);
    recv_len = recv(sock, recbuf, RECV_DATAMAX, 0);
    if ((recv_len > 0) && (!ulreturn))
    {
        printf("%s\r\n", recbuf);
    }
    else
    {
        printf("Receive error.\r\n");
        break;
    }
}

    3 socket同时收发

    我尝试了几种方法,也就是踩过的几个坑,都展示出来:

    1

    使用两个独立的socket连接tcp,分别做发送/接收功能

    问题是接收部分能精准的识别到client和server断开连接,发送部分不能识别到,不能进行重连;

    2

    使用一个socket做tcp连接,发送轮询,单独开接收任务

    问题是发送部分不能识别到,不能进行重连,接收也会失效

    3

    使用一个socket做tcp连接,接收轮询,单独开发送任务

    能识别到重新连接,但是链接后会重复发送两包相同数据;(使用了接收对断开连接敏感的特性)

    后发现是因为断一次后,socket连接tcp时,又单独开了发送任务。

    4

    使用一个socket做连接tcp,分时段轮询tcp发送接收,使用fdassert做500us的延时判断接收

    发送通过消息队列进行发送,发送可被其他调用,消息队列改大造成hardfault;

    5

    使用一个socket做连接tcp,分时段轮询tcp发送接收,使用fdassert做500us的延时判断接收

    发送通过消息队列进行发送,发送可被其他调用;使用消息队列结构体做独立的发送部分,建立新的消息队列

    总结来说:TCP发送(write)和接收(recv)两个部分对 TCP 连接断开检测的灵敏度完全不同。

    1. 接收任务调用 recv(),这是被动等待的函数。当 Server 断开连接时:recv() 会立即返回 0(表示"连接被对方正常关闭"),当你的代码检测到 recv_len == 0 后,立即跳出内层循环,关闭 socket,进入外层循环重连,这意味着接收任务能在断开发生后的第一时间感知,并马上执行重连逻辑。
    2. 发送任务调用 send(),这是主动推送的函数。当 Server 断开连接时(比如"拔网线"或突然断电),存在一个致命的盲区:TCP 协议栈不知道连接已经断开,仍然认为连接是正常的。因此: send() 可能返回成功(数据进了发送缓冲区,但并未真正到达对端)或者 send() 长时间阻塞/等待,直到 TCP 的重传机制超时(这个时间可能长达几十秒甚至几分钟),在此期间,send() 可能持续返回正值(虽然数据根本没发出去),导致内层循环永远不退出。

    3.1 方法一

    是上表中的第3条:使用一个socket做tcp连接,接收轮询,单独开发送任务。

    关键步骤是:

    1. 在socket connect成功之后创建独立的线程(共享同一个socket),用于client定期发送消息,发送线程中如果发送失败通过信号量告知接收线程;
    2. 在接收时判断信号量和接收状态,如果接收或者发送感知到失败则会断开重连server。

    附上原码:

    /*******************************************************************************
    * @copyright: jxxf
    * @filename:  client.c
    * @brief:     This file provides the client socket example.
    * @author:    jxxf
    * @version:   V1.0.0/2024-7-10
    *             1.Initial version
    * @log:
    *******************************************************************************/
    
    
    /* Includes ------------------------------------------------------------------*/
    #include "client.h"
    #include "lwip/opt.h"
    #include "lwip/sys.h"
    #include "lwip/api.h"
    #include <lwip/sockets.h>
    
    /* Private typedef -----------------------------------------------------------*/
    /* Private define ------------------------------------------------------------*/
    #define RECV_DATAMAX (1024)
    /* Private macro -------------------------------------------------------------*/
    /* Private variables ---------------------------------------------------------*/
    static SemaphoreHandle_t Binary_Sem = NULL;
    char recbuf[RECV_DATAMAX];
    /* Private function prototypes -----------------------------------------------*/
    static void client(void *thread_param);
    static void client_send(void* sock);
    /* Private functions ---------------------------------------------------------*/
    
    /**
      * @brief  Connect to server port and receive data.
      * @param  thread_param:pass a parameter pointer contains information.
      * @retval None
      */
    static void client(void *thread_param)
    {
        int recv_len = 0;
        BaseType_t ulreturn = 0;
        int sock = -1;
        struct sockaddr_in client_addr;
        ip4_addr_t ipaddr;
        printf("Dest IP Addr:%d.%d.%d.%d \t Port No:%d\n\n", \
               DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3, DEST_PORT);
        printf("Set your computer's upper computer to TCP Server. In the User/arch/sys_arch.h file,"
               "change the destination IP address to the IP address on your computer\n\n");
        printf("Modify the corresponding Macro:DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3,DEST_PORT\n\n");
        IP4_ADDR(&ipaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3);
    
        while (1)
        {
            sock = socket(AF_INET, SOCK_STREAM, 0);
    
            if (sock < 0)
            {
                printf("Socket error\n");
                vTaskDelay(10);
                continue;
            }
    
            client_addr.sin_family = AF_INET;
            client_addr.sin_port = htons(DEST_PORT);
            client_addr.sin_addr.s_addr = ipaddr.addr;
            memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));
    
            if (connect(sock,
                        (struct sockaddr *)&client_addr,
                        sizeof(struct sockaddr)) == -1)
            {
                printf("Connect failed!\n");
                closesocket(sock);
                vTaskDelay(2000);
                continue;
            }
    
            printf("Connect to server successful!\n");
            sys_thread_t Client_Send = sys_thread_new("client_send", client_send, &sock, 512, 4);
    
            while (1)
            {
                ulreturn = xSemaphoreTake(Binary_Sem, 0);
                recv_len = recv(sock, recbuf, RECV_DATAMAX, 0);
    
                if ((recv_len > 0) && (!ulreturn))
                {
                    printf("%s\r\n", recbuf);
                }
                else if (recv_len == 0)
                {
                    printf("Server closed the connection.\r\n");
                    break;
                }
                else
                {
                    printf("Receive error.\r\n");
                    break;
                }
            }
    
            closesocket(sock);
            vTaskDelete(Client_Send);
        }
    }
    
    /**
      * @brief  Send data to server periodically.
      * @param  thread_param:pass a parameter pointer contains information.
      * @retval None
      */
    static void client_send(void* sock)
    {
        int send_sock = *(int*)sock;
        uint8_t send_buf[] = "STM32F4xx Eval TCP Client test...\n";
    
        while (1)
        {
            if (write(send_sock, send_buf, sizeof(send_buf)) < 0)
                while (pdFALSE == xSemaphoreGive(Binary_Sem));
    
            vTaskDelay(1000);
        }
    }
    
    /**
      * @brief  Create a TCP client task.
      * @param  None
      * @retval None
      */
    void client_init(void)
    {
        Binary_Sem = xSemaphoreCreateBinary();
        sys_thread_new("client", client, NULL, 512, 4);
    }
    

    3.2 方法二

    是上表中的第5条:使用一个socket做连接tcp,分时段轮询tcp发送接收,使用fdassert做500us的延时判断接收。

    关键步骤是:

    1. 在socket connect成功之后,做一个多路IO复用fd_set,扫描选定socket是否有接收事件发生;
      int ret = select(sock + 1, &readfds, NULL, NULL, &tv);
    2. 如果有事件发生,判断是否为这个socket的。如果都符合,进入接收流程。
    3. 发送通过消息队列进行发送,可以通过其他任务异步发送消息(消息队列结构体创建为指针形式,需要注意堆得分配和释放时机);

    附上原码:

    /*******************************************************************************
    * @copyright: jxxf
    * @filename:  client.c
    * @brief:     This file provides the client socket example.
    * @author:    jxxf
    * @version:   V1.0.0/2026-6-16
    *             1.Initial version
    * @log:
    *******************************************************************************/
    
    
    /* Includes ------------------------------------------------------------------*/
    #include "client.h"
    #include "lwip/opt.h"
    #include "lwip/sys.h"
    #include "lwip/api.h"
    #include <lwip/sockets.h>
    
    /* Private typedef -----------------------------------------------------------*/
    typedef struct {
        uint8_t *data;
        size_t len;
    } send_msg_t;
    
    /* Private define ------------------------------------------------------------*/
    #define SEND_QUEUE_LEN    4
    #define RECV_DATAMAX      1024
    
    /* Private macro -------------------------------------------------------------*/
    /* Private variables ---------------------------------------------------------*/
    char recbuf[RECV_DATAMAX];
    const char send_buf[] = "STM32F4xx Eval TCP Client test...\n";
    
    //SemaphoreHandle_t BinarySem_Handle;
    static QueueHandle_t send_queue = NULL;
    /* Private function prototypes -----------------------------------------------*/
    static void client(void *thread_param);
    static void client_generic_send(void *thread_param);
    static int tcp_send_async(const void *data, size_t len);
    /* Private functions ---------------------------------------------------------*/
    
    
    /**
      * @brief  Transmit data periodically.
      * @param  thread_param:pass a parameter pointer contains information。
      * @retval None
      */
    static void client_generic_send(void *thread_param)
    {
        while (1)
        {
            tcp_send_async(send_buf, sizeof(send_buf));
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    
    /**
      * @brief  Connect to server port and transmit data periodically.
      * @param  thread_param:pass a parameter pointer contains information。
      * @retval None
      */
    static void client(void *thread_param)
    {
        int sock = -1;
        send_msg_t send_msg;
        struct sockaddr_in client_addr;
        uint32_t last_send = 0;
        ip4_addr_t ipaddr;
        printf("Dest IP Addr:%d.%d.%d.%d \t Port No:%d\n\n", \
               DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3, DEST_PORT);
        printf("Set your computer's upper computer to TCP Server. In the User/arch/sys_arch.h file,"
               "change the destination IP address to the IP address on your computer\n\n");
        printf("Modify the corresponding Macro:DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3,DEST_PORT\n\n");
        IP4_ADDR(&ipaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3);
    
        while (1)
        {
            if (sock < 0)
            {
                sock = socket(AF_INET, SOCK_STREAM, 0);
    
                if (sock < 0)
                {
                    printf("Socket error\n");
                    vTaskDelay(10);
                    continue;
                }
    
                client_addr.sin_family = AF_INET;
                client_addr.sin_port = htons(DEST_PORT);
                client_addr.sin_addr.s_addr = ipaddr.addr;
                memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));
    
                if (connect(sock, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) == -1)
                {
                    printf("Connect failed!\n");
                    closesocket(sock);
                    vTaskDelay(2000);
                    continue;
                }
    
                printf("Connect to server successful!\n");
                while (xQueueReceive(send_queue, &send_msg, 0) == pdPASS)
                {
                    vPortFree(send_msg.data);
                }
            }
    
            /* Waiting event: The socket is readable or has sent a message */
            fd_set readfds;
            struct timeval tv = {0, 500};
            FD_ZERO(&readfds);
            FD_SET(sock, &readfds);
            int ret = select(sock + 1, &readfds, NULL, NULL, &tv);
    
            /* handle receive */
            if (ret > 0 && FD_ISSET(sock, &readfds))
            {
                int len = recv(sock, recbuf, sizeof(recbuf) - 1, 0);
    
                if (len > 0)
                {
                    recbuf[len] = '\0';
                    printf("Recv: %s\r\n", recbuf);
                }
                else
                {
                    close(sock);
                    sock = -1;
                    printf("Receive error.\r\n");
                    continue;
                }
            }
    
            /* Handle sent messages (non-blocking queue fetching) */
            while (xQueueReceive(send_queue, &send_msg, 0) == pdPASS)
            {
                int sent = send(sock, send_msg.data, send_msg.len, 0);
                vPortFree(send_msg.data);
    
                if (sent < 0)
                {
                    close(sock);
                    sock = -1;
                    break;
                }
            }
    
            /* Regular heartbeat,need server perfection mechanism */
            if (sys_now() - last_send >= 30000)
            {
                const char *hb = "PING";
                send(sock, hb, strlen(hb), 0);
                last_send = sys_now();
            }
        }
    }
    
    /**
      * @brief  Client user generic send msg.
      * @param  data:  point to const msg that want to send.
      * @param  len:   the length of const msg.
      * @retval if transmit ok return the len, otherwise return -1.
      */
    static int tcp_send_async(const void *data, size_t len)
    {
        send_msg_t msg;
        if (send_queue == NULL) return -1;
    
        /* Copy the data to the message */
        msg.data = pvPortMalloc(len);
    
        if (msg.data == NULL) return -1;
    
        memcpy(msg.data, data, len);
        msg.len = len;
        
        /* Send to the queue (non-blocking) */  
        if (xQueueSend(send_queue, &msg, 0) != pdPASS)
        {
            vPortFree(msg.data);
            return -1;
        }
        return len;
    }
    
    /**
      * @brief  Create a TCP client task.
      * @param  None
      * @retval None
      */
    void client_init(void)
    {
        send_queue = xQueueCreate(SEND_QUEUE_LEN, sizeof(send_msg_t));
        sys_thread_new("client", client, NULL, 512, 4);
        sys_thread_new("client_generic_send", client_generic_send, NULL, 512, 4);
    }
    

    4. 更好的办法

    实现client和server之间的心跳机制(需要双方修改应用层),这是最灵活和可控的方式。双方约定一个特定的“心跳数据包”(通常是空包或极短的固定指令,如 PING / PONG)。

    流程:

    1. Client启动定时器(如每隔xx秒),调用 lwip_send() 发送心跳请求。
    2. Server 收到后,立即调用 lwip_send() 回复心跳响应。
    3. 如果 Server 在超时时间(如连续 3 个周期)内未收到心跳,则主动调用 lwip_close() 释放连接。反之,如果 Client 未收到 Server 的响应,也判定连接失效,尝试重连。

            本文章针对在嵌入式环境下使用lwip的工程师,本文纯属个人理解,若有更好的方法,希望大家不吝赐教。

    STM32 结合LwIP 实现网线状态智能检测与自动重连 本文详细介绍了如何在STM32上结合LwIP实现网线状态的智能检测与自动重连功能。通过分析LwIP链路检测机制,提供实战代码示例,解决嵌入式设备网络连接不稳定的问题,特别适用于工业现场和智能家居等需要高可靠网络连接的场景。 阅读详情

    相关推荐

    STM32F407+LWIP网络断线自救指南:手把手教你配置KeepAlive与自动重连(附完整代码)

    本文详细介绍了STM32F407与LWIP协议栈在网络断线情况下的自救方案,重点讲解了KeepAlive机制配置与自动重连实现。通过实战代码演示,帮助开发者解决工业场景中TCP连接不稳定问题,提升设备网络可靠性。

    weixin_30920853的博客 396

    STM32F407 LWIP掉线重连

    STM32F407 LWIP掉线重连STM32CUBE配置(简略)网卡配置(注意网卡复位引脚)LWIP配置TCP/IP 连接自动重连的实现Lwip协议栈TCP保活(KeepAlive)设定自动重连流程简介代码实现 STM32CUBE配置(简略) 请根据硬件自行百度网卡、lwip、freertos配置 网卡配置(注意网卡复位引脚) LWIP配置 这里使用静态IP地址,注意一定要把LWIP_NETIF_LINK_CALLBACK选上,不然连接状态改变不能进入拔下或者插入网线回调函数,里面做一点自己的事情

    笔记用 1万+

    STM32+LWIP TCP Client实战:FreeRTOS下的稳定连接与内存优化

    嵌入式网络开发中,STM32通过以太网PHY与MAC控制器完成物理层和链路层通信,而LWIP协议栈则负责TCP/IP网络层与传输层。在多任务场景下,FreeRTOS的调度机制与LWIP的周期处理、内存管理如何高效协同,是决定TCP Client稳定性的核心。围绕CubeMX配置、netconn API编程、连接状态机设计和消息队列通信,系统分析了PBUF池、TCP窗口等关键参数的调优策略,并结合实际工程案例,深入排查断线重连失败、数据积压、内存耗尽等高频问题。适用于物联网设备、工业数据采集等需要长时间可靠T

    weixin_29168813的博客 290

    STM32被拔网线 LWIP的TCP无法重连解决方案

    一开始的解决思路就是在tcp的recv下面加一个检测网线是否被插入的判断,如果网线被拔出的话,也break,跳出当前while去上一级while里面进行tcp连接,可是忽略了recv函数阻塞的问题,网线被拔出recv没有数据根本不往下执行,如果是netconn不阻塞的那种倒是可以,所以这个方案否了。后来琢磨recv不是阻塞么,不如重新创建一个任务检测网口的网线插入状态,算是解决了网线拔出再插入以后tcp不能重新建立连接的问题,可能方法过于简单粗暴,如果大佬有更好的方法解决这个问题欢迎交流指导。

    千千道的博客 6400

    STM32+FreeRTOS下LWIP TCP Client断线重连避坑指南

    嵌入式网络通信中,TCP/IP协议栈的稳定性直接影响设备可靠性。LWIP作为轻量级协议栈,常与FreeRTOS等实时操作系统配合,在STM32等MCU上实现网络功能。然而,实际工程中常遇到内存配置不当、任务优先级冲突、断线重连机制缺失等问题,导致TCP Client连接异常、设备死机。本文从CubeMX环境配置、lwipopts.h内存参数、socket编程及断线重连排查链路出发,深入分析LWIP在STM32+FreeRTOS上的应用要点,帮助工程师规避常见陷阱,实现稳定可靠的TCP通信。适用于工业网关、数

    weixin_31858171的博客 233

    STM32+FreeRTOS实战:LWIP 2.1.2的TCP Client两种实现方式对比(附完整代码)

    本文详细对比了在STM32+FreeRTOS环境下,使用LWIP 2.1.2实现TCP Client的两种方式:Netconn API与Socket API。通过实战代码,从内存占用、执行效率、代码复杂度及可移植性等多维度深入分析,为嵌入式网络开发中的API选型提供清晰决策指南。

    x8y9z0的博客 900

    STM32F107+FreeRTOS下LwIP移植与稳健Socket Client实现详解

    在嵌入式系统开发中,实时操作系统(RTOS)与轻量级TCP/IP协议栈(LwIP)的结合,是实现设备网络通信的基石。其核心原理在于通过操作系统抽象层(sys_arch)将协议栈与任务调度、同步机制进行适配,从而在资源受限的MCU上提供可靠的数据传输能力。这一技术的核心价值在于为物联网终端、工业数据采集等场景提供了稳定、高效的网络连接解决方案。在实际应用中,开发者常面临协议栈移植不稳定、长连接维护困难等挑战。本文聚焦于STM32F107平台,深入剖析了LwIP在FreeRTOS上的关键移植要点,如线程优先级与

    weixin_30565199的博客 352

    STM32+FreeRTOS实战:LWIP 2.1.2的TCP Client避坑指南(附完整代码)

    本文针对STM32+FreeRTOS平台下使用LWIP 2.1.2实现TCP Client的实战开发,提供了详尽的避坑指南。文章深入剖析了LWIP配置陷阱、内存管理、任务优先级设计等关键问题,并对比了Netconn与Socket两种API的实现路径,旨在帮助开发者构建稳定可靠的嵌入式网络连接,附有可直接使用的完整代码框架。

    weixin_29209805的博客 404

    使用stm32cubeide stm32f407 lan8720a freertos lwip 实现tcp客户端、服务端及网络数据转串口数据过程详解

    项目需要使用MCU实现网络功能,后续确定方案stm32f407 外接lan8720a实现硬件平台搭建,针对lan8720a也是用的比较多的phy,网上比较多的开发板,硬件上都是选用了这个phy,项目周期比较短,选用了这个常用phy。记得十年前,刚参加工作那会接触的第一个项目,就是MCU实现网络控制协议,当时lwip也不是很流行呢,网络协议栈只能手撕了,当时硬着头皮写,最多也写到UDP,不过对付当时的项目也够用了。主要是采用静态IP,不开启DHCP,针对电脑进行ping包测试。电脑的的ping包测试。

    大牛攻城狮的专栏 1691

    STM32 LWIP网络异常检测:Server与Client的三种高效策略

    本文针对STM32 LWIP网络开发中常见的连接异常问题,详细解析了三种高效的网络异常检测策略:TCP Keepalive心跳机制、直接读取PHY芯片寄存器以及查询LWIP netif接口状态。文章对比了各策略的检测层级、速度与适用场景,并推荐组合使用物理链路快速感知与TCP保活机制,以构建稳定的嵌入式网络应用,有效提升STM32作为Server或Client的连接可靠性。

    hope8的博客 455

    STM32 LwIP TCP Client长数据发送完整指南:从ERR_MEM到稳定传输

    在嵌入式网络开发中,TCP/IP协议栈的可靠传输是设备万物互联的基础。LwIP作为轻量级协议栈在STM32等MCU上广泛应用,但很多开发者发现短数据收发顺畅,一旦涉及KB级以上的长数据发送,就容易遭遇tcp_write返回ERR_MEM、传输中断或吞吐骤降等问题。这背后的本质是协议栈内存模型与应用层发送节奏的失配:TCP发送缓冲上限、内核内存池大小以及ACK确认机制共同决定了数据队列的容量。理解这些原理后,合理配置TCP_SND_BUF与MEM_SIZE,并采用由tcp_sndbuf和tcp_sent回调驱

    weixin_42561143的博客 242

    STM32H7 FreeRTOS LWIP TCP客户端示例

    STM32H7 FreeRTOS LWIP TCP客户端示例 【下载地址】STM32H7FreeRTOSLWIPTCP客户端示例 本仓库提供了一个已经验证并移植好的LWIP示例,基于STM32H743微控制器,作为TCP客户端使用。该示例实现了自收自发的TCP通信,运行在FreeRTOS操作系统上 ...

    gitblog_09792的博客 1098

    告别裸机思维:用CubeMX+LwIP在STM32上构建你的第一个轻量级网络服务(TCP Server/Client实战)

    本文详细介绍了如何使用CubeMX和LwIP协议栈在STM32平台上构建轻量级网络服务,包括TCP Server和Client的实战开发。通过硬件选型、CubeMX配置、LwIP协议栈解析及TCP通信实现,帮助开发者快速掌握嵌入式网络编程技术,实现设备间的智能通信。

    weixin_33716941的博客 400

    STM32F429平台FreeRTOS+LwIP2.1.2移植与TCP Client实现工程:助力物联网开发的高效工具

    STM32F429平台FreeRTOS+LwIP2.1.2移植与TCP Client实现工程:助力物联网开发的高效工具 【下载地址】STM32F429平台FreeRTOSLwIP2.1.2移植与TCPClient实现工程 本项目提供了一个完整的STM32F429平台上的FreeRTOS与LwIP2.1.2移植工程,并实现...

    gitblog_06769的博客 827

    LwIP HTTP Client改造实战:为STM32等MCU添加POST请求功能

    HTTP协议是物联网设备与云端通信的基础,其GET和POST方法是核心操作。GET用于获取数据,而POST则用于提交数据,这在设备主动上报传感器数据时至关重要。在嵌入式领域,LwIP作为轻量级TCP/IP协议栈,其内置的HTTP Client示例通常只支持GET请求,这限制了设备的数据上报能力。通过理解LwIP基于回调函数的事件驱动模型,我们可以对其内部请求构造流程进行精准扩展,而非重写。这种改造方案的技术价值在于,能以最小的侵入性,让原有框架同时支持GET和POST,显著提升了嵌入式设备在物联网应用中的交

    weixin_33866037的博客 417

    手把手教你用OpenCV+Python实现HALCON级模板匹配(附工业案例代码)

    本文详细介绍了如何利用开源计算机视觉库OpenCV和Python,构建媲美HALCON商业软件的工业级模板匹配解决方案。通过分析工业检测的核心挑战,文章提供了从模板预处理、多策略融合匹配到应对尺度与旋转的完整代码实现,并附有连接器引脚检测的实战案例,为工业自动化场景下的高精度视觉检测提供了低成本、高可控性的技术路径。

    e3f4g5的博客 284

    STM32F107-lwip-UDP-client发送数据:打造稳定高效的UDP通信解决方案

    STM32F107-lwip-UDP-client发送数据:打造稳定高效的UDP通信解决方案 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在现代嵌入式开发领域,网络通信能力是衡量项目成功与否的关键因素之一。STM32F107-lwip-UDP-client发送数据项目,正是为此而设计。它是一个开源的UDP客户端发送数据示例,专为STM32F107开发板打造,集成了l...

    gitblog_06780的博客 432

    AutoDock Vina 对接计算(大批量)

    AutoDock Vina是一种用于计算生物分子之间相互作用的计算工具,主要用于蛋白质和小分子(通常是潜在药物分子)之间的对接研究。它可以用于虚拟筛选、药物设计和生物分子结合位点的预测等多个应用领域。

    MurphyStar的博客 1万+

    【亲测免费】 探索STM32F429上的TCP通信新境界:lwip2.1.2与FreeRTOS的完美结合

    探索STM32F429上的TCP通信新境界:lwip2.1.2与FreeRTOS的完美结合 【下载地址】STM32F429LWIP2.1.2FreeRTOSTCPClientServer资源文件 本资源文件包含了在STM32 FreeRTOS平台上移植的lwip2.1.2库,并成功实现了基于lwip2.1.2的TCP C...

    gitblog_09792的博客 911

    VECTOR硬件VN1600系列对比:VN1610、VN1611、VN1630A、VN1640A、VN1630log、VN1670

    VN16系列说不清道不明的区别和关系

    AmorRun的博客 2万+

    Python 上位机开发

    首先,什么是上位机?上位机通常是指可以与下位机(如单片机、传感器等硬件设备)进行通信和交互,实现数据采集、控制指令发送以及数据处理和展示的计算机程序。 Python 作为一种高级编程语言,在开发上位机方面具有诸多优势。它拥有丰富的第三方库,使得开发过程变得相对简单和高效。

    weixin_46807151的博客 4303

    野火挑战者V2开发板CubeMX+LWIP+FreeRTOS+TCP_Server+TCP_Client实现

    野火挑战者V2开发板CubeMX+LWIP+FreeRTOS+TCP_Server+TCP_Client实现

    qq_44712722的博客 3575

    ESP8266+ITR8307实战:如何用反射式光电管实现5cm远距离检测(附避坑指南)

    本文详细介绍了如何利用ESP8266的ADC功能与ITR8307反射式光电管,通过调制解调、硬件电路优化及软件同步采样策略,实现5cm稳定远距离检测。文章提供了完整的硬件设计、代码实现与调优避坑指南,有效解决了环境光干扰与信号衰减难题,适用于智能车、自动化等需要可靠非接触检测的项目。

    rgv234567的博客 264
    上一篇: IAR工程通过修改icf文件链接函数到指定位置
    LLdalin
    博客等级 码龄7年 319粉丝 8原创
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    个

    红包个数最小为10个

    元

    红包金额最低5元

    当前余额3.43元 前往充值 >
    需支付:10.00元
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付元
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值