【深度】Zynq lwip怎么既可以接收又可以发送呢?
扫描二维码
随时随地手机看文章
前言
最近在做Zynq的ps端,需要用到网络传输,遇到一些问题,在这里汇总一下。有些lwip的发送与接收函数中已经加了锁,我们翻阅底层函数是可以看到的,所以发送接收不会冲突,本篇中,我们就没加锁了。
客户端与服务器共存?
言归正传,我们知道在sdk的例程中,既有做客户端client,又有做服务器server 的,那么Zynq lwip怎么既可以做客户端又可以做服务器呢?
简而言之,在同一个连接中,怎么做到既可以接收又可以发送呢?
以udp协议为例
我们以udp协议为例吧。先看看怎么发送数据到pc端。
在我们建立sock连接之后,当有数据需要发送时,则触发事件,进行发送,在taskUdpSendHandleEvent(event);函数中操作。
1void taskUdpSendMesg(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 udpPacketSend(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 taskUdpRecvMesg(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的原理,这样很多问题就会迎刃而解。





