【深度】Zynq lwip怎么既可以接收又可以發(fā)送呢?
掃描二維碼
隨時隨地手機看文章
前言
最近在做Zynq的ps端,需要用到網(wǎng)絡傳輸,遇到一些問題,在這里匯總一下。有些lwip的發(fā)送與接收函數(shù)中已經(jīng)加了鎖,我們翻閱底層函數(shù)是可以看到的,所以發(fā)送接收不會沖突,本篇中,我們就沒加鎖了。
客戶端與服務器共存?
言歸正傳,我們知道在sdk的例程中,既有做客戶端client,又有做服務器server 的,那么Zynq lwip怎么既可以做客戶端又可以做服務器呢?
簡而言之,在同一個連接中,怎么做到既可以接收又可以發(fā)送呢?
以udp協(xié)議為例
我們以udp協(xié)議為例吧。先看看怎么發(fā)送數(shù)據(jù)到pc端。
在我們建立sock連接之后,當有數(shù)據(jù)需要發(fā)送時,則觸發(fā)事件,進行發(fā)送,在taskUdpSendHandleEvent(event);函數(shù)中操作。
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); /* 設(shè)置最大等待時間500ms */ 30 Event event; 31 while(1) 32 { 33 taskUdpSendHandleEvent(event); 34 }
udp發(fā)送最主要的是lwip_sendto函數(shù),當然下面這個還有一個簡單的重發(fā)機制。
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 }
在說完了發(fā)送之后,那么怎么在發(fā)送的時候建立接收機制呢?
我們再開另外一個線程,產(chǎn)生一個新的線程,即進入主線程,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添加網(wǎng)絡接口,并將其設(shè)置為默認接口*/ 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 /* 啟用網(wǎng)絡接口 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()函數(shù)單獨開啟一個線程,將從中斷響應過程中接收到的數(shù)據(jù)包移植到lwip的* xemacif_input_thread()函數(shù)運行的線程中,該線程在lwip數(shù)據(jù)包到達時發(fā)出通知,并接收中斷句柄將數(shù)據(jù)存儲到緩存中*/ 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}
上述函數(shù)vTaskUdpRecvMesg()中在中我們開始做這接收的操作,重點是我們需要申明一個不同的sock,定義如下:
1static int sock1[NUM_OF_PARALLEL_CLIENTS];
接收端主要是lwip_recvfrom()函數(shù),當然lwip_recv()函數(shù)也是一樣的,只是形參的區(qū)別。
我們在建立好連接之后,如果pc端有數(shù)據(jù)發(fā)送,則會在recv_buf里接收到,這里我把數(shù)據(jù)發(fā)送出去處理,如果沒有數(shù)據(jù)接收,那么會堵塞在接收函數(shù)中,這樣就可以解決既可以接收又可以發(fā)送的問題了。
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}
總結(jié)
在項目中,我們經(jīng)常會遇到這樣的問題,第一點使用操作系統(tǒng),第二點根據(jù)需求抓住難點,重點還是要理解udp/tcp的原理,這樣很多問題就會迎刃而解。





