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
相关产品推荐
相关产品推荐

