电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【深度】Zynq lwip怎么既可以接收又可以发送呢?
分 享
扫描二维码分享
【深度】Zynq lwip怎么既可以接收又可以发送呢?
lwip
C语言
李肖遥
关注
发布时间: 2020-08-24
丨
阅读: 2618
## 前言 最近在做Zynq的ps端,需要用到网络传输,遇到一些问题,在这里汇总一下。有些lwip的发送与接收函数中已经加了锁,我们翻阅底层函数是可以看到的,所以发送接收不会冲突,本篇中,我们就没加锁了。 ## 客户端与服务器共存? 言归正传,我们知道在sdk的例程中,既有做客户端client,又有做服务器server 的,那么Zynq lwip怎么既可以做客户端又可以做服务器呢? 简而言之,在同一个连接中,怎么做到既可以接收又可以发送呢? ## 以udp协议为例 我们以udp协议为例吧。先看看怎么发送数据到pc端。 在我们建立sock连接之后,当有数据需要发送时,则触发事件,进行发送,在taskUdpSendHandleEvent(event);函数中操作。 ``` 1void vTaskUdpSendMesg(void *pPara,QueueHandle_t evntQueue) 2{ 3 err_t err; 4 u32_t i; 5 6 memset(&addr, 0, sizeof(struct sockaddr_in)); 7 addr.sin_family = AF_INET; 8 addr.sin_port = htons(UDP_CONN_PORT); 9 addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDRESS); 10 11 for (i = 0; i < NUM_OF_PARALLEL_CLIENTS; i++) { 12 if ((sock[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 13 xil_printf("UDP client: Error creating Socket\r\n"); 14 return; 15 } 16 17 err = connect(sock[i], (struct sockaddr *)&addr, sizeof(addr)); 18 if (err != ERR_OK) { 19 xil_printf("UDP client: Error on connect: %d\r\n", err); 20 close(sock[i]); 21 return; 22 } 23 } 24 25 /@@* Wait for successful connections */ 26 usleep(10); 27 28 reset_stats(); 29 const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /@@* 设置最大等待时间500ms */ 30 Event event; 31 while(1) 32 { 33 taskUdpSendHandleEvent(event); 34 } ``` udp发送最主要的是lwip_sendto函数,当然下面这个还有一个简单的重发机制。 ``` 1static int udp_packet_send(u16* buffer,int length) 2{ 3 u8_t retries=0; 4 int i, count; 5 socklen_t len = sizeof(addr); 6 count = lwip_sendto(sock[0], buffer, length, 0, 7 (struct sockaddr *)&addr, len);//return sizeof(send_buf) 8 //printf("count value is : %d \r\n",count); 9 if (count <= 0) { 10 retries = MAX_SEND_RETRY; 11 xil_printf("enter udp_packet_send\r\n\r\n, "); 12 usleep(ERROR_SLEEP); 13 } 14 for (i = 0; i < NUM_OF_PARALLEL_CLIENTS; i++) { 15 while (retries) { 16 xil_printf("retries value is %d\r\n\r\n",retries); 17 count = lwip_sendto(sock[0], buffer, length, 0, 18 (struct sockaddr *)&addr, len);//return sizeof(send_buf) 19 if (count <= 0) { 20 retries--; 21 usleep(ERROR_SLEEP); 22 } else { 23 client.total_bytes += count; 24 client.cnt_datagrams++; 25 client.i_report.total_bytes += count; 26 break; 27 } 28 } 29 } ``` 在说完了发送之后,那么怎么在发送的时候建立接收机制呢? 我们再开另外一个线程,产生一个新的线程,即进入主线程,complete_nw_thread标志置为1,代码如下: ``` 1void NetworkRecvThread(void *pPara) 2{ 3 /@@* the mac address of the board. this should be unique per board */ 4 u8_t mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 }; 5 6 /@@* Add network interface to the netif_list, and set it as default */ 7 /@@*xemac_add添加网络接口,并将其设置为默认接口*/ 8 if (!xemac_add(&server_netif, NULL, NULL, NULL, mac_ethernet_address, 9 PLATFORM_EMAC_BASEADDR)) { 10 xil_printf("Error adding N/W interface\r\n"); 11 return; 12 } 13 14 netif_set_default(&server_netif); 15 16 /@@* 启用网络接口 specify that the network if is up */ 17 netif_set_up(&server_netif); 18 19 /@@* start packet receive thread - required for lwIP operation */ 20 /@@*为xemacif_input_thread()函数单独开启一个线程,将从中断响应过程中接收到的数据包移植到lwip的* xemacif_input_thread()函数运行的线程中,该线程在lwip数据包到达时发出通知,并接收中断句柄将数据存储到缓存中*/ 21 sys_thread_new("xemacif_input_thread", 22 (void(*)(void*))xemacif_input_thread, &server_netif, 23 THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); 24 complete_nw_thread = 1; 25 26 //receive task begin 27 vTaskUdpRecvMesg(&pPara); 28 29 vTaskDelete(NULL); 30 31} ``` 上述函数vTaskUdpRecvMesg()中在中我们开始做这接收的操作,重点是我们需要申明一个不同的sock,定义如下: ``` 1static int sock1[NUM_OF_PARALLEL_CLIENTS]; ``` 接收端主要是lwip_recvfrom()函数,当然lwip_recv()函数也是一样的,只是形参的区别。 我们在建立好连接之后,如果pc端有数据发送,则会在recv_buf里接收到,这里我把数据发送出去处理,如果没有数据接收,那么会堵塞在接收函数中,这样就可以解决既可以接收又可以发送的问题了。 ``` 1void vTaskUdpRecvMesg(void *pPara) 2{ 3 QueueHandle_t taskUdpRecvQueue; 4 taskUdpRecvQueue = getTaskQueue(getTaskId("PsSendOrder2PlTask")); 5 //BaseType_t xHigherPriorityTaskWoken = pdFALSE; 6 //create a event, send it 7 Event event; 8 int count; 9 struct sockaddr_in from; 10 socklen_t fromlen = sizeof(from); 11 while (1) { 12 if((count = lwip_recvfrom(sock1[0], recv_buf, UDP_SEND_BUFSIZE, 0, 13 (struct sockaddr *)&from, &fromlen)) > 0) 14 { 15 makeEvent(&event,UdpRec_ID,psSendData2pl,strlen(recv_buf),recv_buf); 16 xQueueSendToBack(taskUdpRecvQueue,&event,0); 17 } 18 } 19} ``` ## 总结 在项目中,我们经常会遇到这样的问题,第一点使用操作系统,第二点根据需求抓住难点,重点还是要理解udp/tcp的原理,这样很多问题就会迎刃而解。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
李肖遥
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交