【ZYNQ MPSoC开发】移植LWIP TCP客户端发送停不下来的问题分析与解决

问题的发现

       大部分新手小白使用ZYNQ的PS端实现TCP客户端时,会参考Xilinx的lwIP TCP Perf Client例程,把它的整体代码搬过来,我再进行相关的项目开发的时候,也是这样,一开始只是想先测测板子实际能跑的带宽,而例程中的transfer_data()函数也确实是放在while(1)中循环运行,就没太关注,而在后面需要在指定的时刻发送数据时,发现哪怕仅仅是执行一次transfer_data()函数,网络调试助手也会不断地收到数据。下面对这个问题进行分析并提出解决方案。

问题分析

       先来看一看整个例程涉及到发送的地方。

在这里插入图片描述

       例程把它放在了while(1)中,只要不是快速定时器与慢速定时器设置的时间到了,都是一直循环运行它以及另外一个处理输入的函数xemacif_input(netif),考虑到这整个例程是一个测速的程序,本身就应该是一直不断地发送数据,这里看上去没有什么问题,那就进入到transfer_data()函数内部看看。

void transfer_data(void)
{
	if (client.client_id)
		tcp_send_perf_traffic();
}

       函数比较简单,即这个client_id不为0时,就运行tcp_send_perf_traffic()。这个client_id通过查找可以发现,它在while(1)上的start_application()函数中进行初始化赋值为0,在连接成功回调函数tcp_client_connected中+1,即连接成功后,发送函数就会不断地运行tcp_send_perf_traffic()。那我们再进入其看看。

static err_t tcp_send_perf_traffic(void)
{
	err_t err;
	u8_t apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;

	if (c_pcb == NULL) {
		return ERR_CONN;
	}

#ifdef __MICROBLAZE__
	/* Zero-copy pbufs is used to get maximum performance for Microblaze.
	 * For Zynq A9, ZynqMP A53 and R5 zero-copy pbufs does not give
	 * significant improvement hense not used. */
	apiflags = 0;
#endif

	while (tcp_sndbuf(c_pcb) > TCP_SEND_BUFSIZE) {
		err = tcp_write(c_pcb, send_buf, TCP_SEND_BUFSIZE, apiflags);
		if (err != ERR_OK) {
			xil_printf("TCP client: Error on tcp_write: %d\r\n",
					err);
			return err;
		}

		err = tcp_output(c_pcb);
		if (err != ERR_OK) {
			xil_printf("TCP client: Error on tcp_output: %d\r\n",
					err);
			return err;
		}
		client.total_bytes += TCP_SEND_BUFSIZE;
		client.i_report.total_bytes += TCP_SEND_BUFSIZE;
	}

	if (client.end_time || client.i_report.report_interval_time) {
		u64_t now = get_time_ms();
		if (client.i_report.report_interval_time) {
			if (client.i_report.start_time) {
				u64_t diff_ms = now - client.i_report.start_time;
				u64_t rtime_ms = client.i_report.report_interval_time;
				if (diff_ms >= rtime_ms) {
					tcp_conn_report(diff_ms, INTER_REPORT);
					client.i_report.start_time = 0;
					client.i_report.total_bytes = 0;
				}
			} else {
				client.i_report.start_time = now;
			}
		}

		if (client.end_time) {
			/* this session is time-limited */
			u64_t diff_ms = now - client.start_time;
			if (diff_ms >= client.end_time) {
				/* time specified is over,
				 * close the connection */
				tcp_conn_report(diff_ms, TCP_DONE_CLIENT);
				xil_printf("TCP test passed Successfully\n\r");
				tcp_client_close(c_pcb);
				c_pcb = NULL;
				return ERR_OK;
			}
		}
	}
	return ERR_OK;
}

​       虽然比较长,但实际也就包含了两个部分,一个部分是当发送buffer足够时,循环地把发送数组大小的数据发送出去,另一部分则是测速部分,我们这里不过多关注。但问题来了,这看上去并没有什么问题,虽然发送buffer足够时会一直进行发送,但其会在buffer不足后跳出while循环,再返回,而例程设置的发送buffer本身也不大,只有65535Bytes,发送数组长度为7300,应该发送8组后就停了下来,正当百思不得其解时,发现在这个函数下面还有一个函数tcp_client_sent

/** TCP sent callback, try to send more data */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	return tcp_send_perf_traffic();
}

       其英文注释写的很明白,这是TCP发送成功的回调函数,而该例程在此直接返回了上面讨论的tcp_send_perf_traffic(),尝试发送更多的数据,即每成功发送出去了7300Bytes后,也会再执行一次发送函数,这也就导致我们把其移植到自己的工程,想根据我们的需要进行发送时,哪怕只执行一次发送函数transfer_data(),也会一直发送数据。

问题的解决

       知道了问题的产生原因,那解决起来就很容易了,我们只要修改发送成功的回调函数tcp_client_sent,将其直接改为一下形式即可:

/** TCP sent callback, try to send more data */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	return;
}
  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辣个蓝人QEX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值