问题描述
LWIP 的 TCP Client 发送数据可以调用 tcp_write() 函数,将数据储存在缓冲区里面,然后等待超时自动发送或者调用tcp_output()函数进行发送。
然而 tcp_write() 函数 需要发送的数据过长时,将无法发送,并返回 ERR_MEM。
tcp_write()函数解析
tcp_write()的函数代码很多,就不细看代码了,
先看tcp_write()的完整函数:
err_t tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
其中 pcb 是TCP协议块 arg 是待发送数据指针 len 是待发送数据长度 apiflags 是发送的方式,如果是1,则发送的时候,会复制数据到缓存进行发送,如果是2,则发送过程中arg的数据不能变化
如何解决发送长度的问题
看官方注释:
* The proper way to use this function is to call the function with at
* most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
* the application should wait until some of the currently enqueued
* data has been successfully received by the other host and try again.
翻译过来的意思就是: 使用此函数的正确方法是使用最多tcp_sndbuf()字节的数据调用该函数。如果函数返回ERR_MEM,应用程序应该等待,直到当前排队的数据被其他主机成功接收,然后再试一次。
其中tcp_sndbuf() 是一个宏定义, 最终是指pcb结构体里的 tcpwnd_size_t snd_buf; /* Available buffer space for sending (in bytes). */ 也就是一次发送的最大字节数。
由此可知,我们发送长数据的时候,只需要连续分包发送,且每次发送小于tcp_sndbuf()字节,就可以实现长数据发送了
最终实现长数据发送函数
最后附上已实现的函数,配合FREERTOS,就可以发送长数据了,实测稳定可用。
int my_tcp_write_eth(uint8_t * Buf,int Size)
{
int try_time = 0;
int send_pack_index = 0;
if(Size == 0)
return 0;
err_t err_status;
int sizemax = 500;
send_pack:
if(try_time >= 150)
{
printf("TCP发送失败! ERR_MEM \n");
return -1;
}
if(Size == 0)
return 1;
if(Size <= sizemax)
{
err_status = tcp_write(client_pcb, &Buf[send_pack_index * sizemax], Size , 1);
if(err_status != ERR_OK)
{
if(err_status == ERR_MEM)
{
osDelay(5);
try_time ++;
goto send_pack;
}
else
printf("TCP发送失败!\n");
return -1;
}
if(tcp_output(client_pcb) != ERR_OK)
{
printf("TCP发送失败!\n");
return -1;
}
return 1;
}
else
{
err_status = tcp_write(client_pcb, &Buf[send_pack_index * 500], 500, 1);
if(err_status != ERR_OK)
{
if(err_status == ERR_MEM)
{
osDelay(5);
try_time ++;
goto send_pack;
}
else
printf("TCP发送失败!\n");
return -1;
}
if(tcp_output(client_pcb) != ERR_OK)
{
printf("TCP发送失败!\n");
return -1;
}
try_time = 0;
Size -= sizemax;
send_pack_index ++;
goto send_pack;
}
}
|