You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arty A7开发板LWIP TCP按钮触发tcp_write发送失败求助

LWIP tcp_write返回ERR_ARG(-16):按键触发主动发送数据失败问题

我在Arty A7开发板上实现了LWIP的Echo Server示例,运行完全正常。之后修改代码使其返回自定义数据,该功能仅在接收回调中可以正常工作。

现在希望通过按键触发,使用tcp_write向电脑发送数据:按键在主循环中被读取,触发transfer_data函数,在该函数中调用tcp_write发送数据。原本认为这应该和接收回调中的用法一样可行,但目前一直无法成功,返回错误码-16(非法参数ERR_ARG)。

以下是修改后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
 
#include "lwip/err.h"
#include "lwip/tcp.h"
#if defined (__arm__) || defined (__aarch64__)
#include "xil_printf.h"
#endif
 
int transfer_data() {
 
    err_t err;
    struct tcp_pcb *tpcb;
 
    xil_printf("transfer data\n\r");
    err = tcp_write(tpcb, "data", 4, 1);
    xil_printf("%d\n\r", err);
    return 0;
}
 
void print_app_header()
{
#if (LWIP_IPV6==0)
    xil_printf("\n\r\n\r-----lwIP TCP echo server ------\n\r");
#else
    xil_printf("\n\r\n\r-----lwIPv6 TCP echo server ------\n\r");
#endif
    xil_printf("TCP packets sent to port 6001 will be echoed back\n\r");
}
 
err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (!p) {
        tcp_close(tpcb);
        tcp_recv(tpcb, NULL);
        return ERR_OK;
    }
 
    tcp_recved(tpcb, p->len);
 
    if (tcp_sndbuf(tpcb) > p->len) {
        xil_printf("data received: %s (%d)\r\n", p->payload, strlen(p->payload));
        char tdata[128];
        memset(tdata, 0, sizeof(tdata));
        char buf[128];
        memset(buf, 0, sizeof(buf));
        memcpy(buf, p->payload, p->len);
        char * cmd = strtok(buf, "\r\n");
        xil_printf("cmd received: %s (%d)\r\n", cmd, strlen(cmd));
 
        while(cmd != NULL) {
            if(strcmp(cmd, "Hello") == 0) {
                strcat(tdata,"World");
                xil_printf("World\n\r");
            }
 
            else if(strcmp(cmd, "TEST") == 0) {
                strcat(tdata,"0123456789");
                xil_printf("Test\n\r");
            }
            cmd = strtok(NULL, "\r\n");
        }
 
        free (cmd);
 
        if(strlen(tdata) > 0) {
            err = tcp_write(tpcb, &tdata, strlen(tdata), 1);
        }
 
    }
    else
        xil_printf("no space in tcp_sndbuf\n\r");
 
    pbuf_free(p);
 
    return ERR_OK;
}
 
err_t sent_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err){
        xil_printf("data were sent\n\r");
        return ERR_OK;
}
 
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    static int connection = 1;
 
    xil_printf("Incoming connection: %d.%d.%d.%d - port: %d \r\n",
                           (u8)(newpcb->remote_ip.addr),
                           (u8)(newpcb->remote_ip.addr >> 8),
                           (u8)(newpcb->remote_ip.addr >> 16),
                           (u8)(newpcb->remote_ip.addr >> 24),
                           newpcb->remote_port);
 
    tcp_recv(newpcb, recv_callback);
    tcp_sent(newpcb, sent_callback);
    tcp_arg(newpcb, (void*)(UINTPTR)connection);
    connection++;
 
    return ERR_OK;
}
 
int start_application()
{
    struct tcp_pcb *pcb;
    err_t err;
    unsigned port = 7;
 
    pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
    if (!pcb) {
        xil_printf("Error creating PCB. Out of Memory\n\r");
        return -1;
    }
 
    err = tcp_bind(pcb, IP_ANY_TYPE, port);
    if (err != ERR_OK) {
        xil_printf("Unable to bind to port %d: err = %d\n\r", port, err);
        return -2;
    }
 
    tcp_arg(pcb, NULL);
    pcb = tcp_listen(pcb);
    if (!pcb) {
        xil_printf("Out of memory while tcp_listen\n\r");
        return -3;
    }
 
    tcp_accept(pcb, accept_callback);
    xil_printf("TCP echo server started @ port %d\n\r", port);
 
    return 0;
}

根据错误信息,推测可能是发送数据未分配内存,或是PCB赋值有误。尝试过分配内存和重新建立仅用于发送的连接,但都没有成功。LWIP文档中的适用示例很少,也搜索过类似问题但未找到合适的解决方案。


问题分析与解决方案

1. 核心问题:未持有有效TCP连接PCB指针

transfer_data函数中的tpcb是未初始化的局部变量,指向随机内存地址,调用tcp_write时必然触发ERR_ARG错误。而接收回调中的tpcb是连接建立后LWIP主动传递的有效连接实例,因此能正常工作。

2. 修正步骤

步骤1:全局存储活跃连接的PCB

添加全局变量保存已建立的TCP连接实例(单连接场景):

struct tcp_pcb *g_active_tpcb = NULL;

步骤2:在连接建立时保存PCB

修改accept_callback,将新建立的连接指针赋值给全局变量:

err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    // ... 原有代码 ...

    // 保存活跃连接PCB(单连接场景,多连接需用链表管理)
    g_active_tpcb = newpcb;

    connection++;
    return ERR_OK;
}

步骤3:修复transfer_data函数逻辑

在发送前检查连接有效性、状态及缓冲区空间,并触发数据发送:

int transfer_data() {
    err_t err;
    // 检查是否有活跃连接
    if (g_active_tpcb == NULL) {
        xil_printf("No active connection\n\r");
        return -1;
    }
    // 检查连接是否处于已建立状态
    if (tcp_state(g_active_tpcb) != ESTABLISHED) {
        xil_printf("Connection not in ESTABLISHED state\n\r");
        return -2;
    }
    // 检查发送缓冲区是否有足够空间
    if (tcp_sndbuf(g_active_tpcb) < 4) {
        xil_printf("Not enough space in send buffer\n\r");
        return -3;
    }

    xil_printf("transfer data\n\r");
    // 使用LWIP宏替代魔法数字,提升可读性
    err = tcp_write(g_active_tpcb, "data", 4, TCP_WRITE_FLAG_COPY);
    if (err == ERR_OK) {
        // 手动触发数据发送,确保数据被推送到网络栈
        tcp_output(g_active_tpcb);
    }
    xil_printf("tcp_write err: %d\n\r", err);
    return 0;
}

步骤4:修复recv_callback中的内存错误

recv_callback中的free(cmd)是错误操作,cmd是strtok返回的栈内存指针,不能用free释放,直接删除该行即可。

内容的提问来源于stack exchange,提问作者Tobias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:33:09