關注嘉友創科技公衆号
- 源碼位址:https://github.com/HX-IoT/ESP32-Developer-Guide
- ESP32開發指南QQ群:824870185,内有pdf版,排版整潔。
學習目的及目标
- 掌握UDP原理和工作過程
- 掌握樂鑫ESP32的UDP的程式設計
- 主要掌握UDP源碼過程
UDP科普(來自百度百科)
UDP 是User Datagram Protocol的簡稱, 中文名是使用者資料報協定,是OSI(Open System Interconnection,開放式系統互聯) 參考模型中一種無連接配接的傳輸層協定,提供面向事務的簡單不可靠資訊傳送服務,IETF RFC 768是UDP的正式規範。UDP在IP封包的協定号是17。
UDP協定全稱是使用者資料報協定,在網絡中它與TCP協定一樣用于處理資料包,是一種無連接配接的協定。在OSI模型中,在第四層——傳輸層,處于IP協定的上一層。UDP有不提供資料包分組、組裝和不能對資料包進行排序的缺點,也就是說,當封包發送之後,是無法得知其是否安全完整到達的。UDP用來支援那些需要在計算機之間傳輸資料的網絡應用。包括網絡視訊會議系統在内的衆多的客戶/伺服器模式的網絡應用都需要使用UDP協定。UDP協定從問世至今已經被使用了很多年,雖然其最初的光彩已經被一些類似協定所掩蓋,但是即使是在今天UDP仍然不失為一項非常實用和可行的網絡傳輸層協定。
與所熟知的TCP(傳輸控制協定)協定一樣,UDP協定直接位于IP(網際協定)協定的頂層。根據OSI(開放系統互連)參考模型,UDP和TCP都屬于傳輸層協定。UDP協定的主要作用是将網絡資料流量壓縮成資料包的形式。一個典型的資料包就是一個二進制資料的傳輸機關。每一個資料包的前8個位元組用來包含報頭資訊,剩餘位元組則用來包含具體的傳輸資料。
UDP是OSI參考模型中一種無連接配接的傳輸層協定,它主要用于不要求分組順序到達的傳輸中,分組傳輸順序的檢查與排序由應用層完成,提供面向事務的簡單不可靠資訊傳送服務。UDP 協定基本上是IP協定與上層協定的接口。UDP協定适用端口分别運作在同一台裝置上的多個應用程式。
UDP提供了無連接配接通信,且不對傳送資料包進行可靠性保證,适合于一次傳輸少量資料,UDP傳輸的可靠性由應用層負責。常用的UDP端口号有:
應用協定 | 端口号 |
DNS | 53 |
TFTP | 69 |
SNMP | 161 |
UDP封包沒有可靠性保證、順序保證和流量控制字段等,可靠性較差。但是正因為UDP協定的控制選項較少,在資料傳輸過程中延遲小、資料傳輸效率高,适合對可靠性要求不高的應用程式,或者可以保障可靠性的應用程式,如DNS、TFTP、SNMP等。
UDP特點和流程
上面的原理很重要,但畢竟我們隻是在API之上做應用。隻需要了解特點和流程。知道特點可以做方案時候考量可行性,流程就是可行後的實施。
UDP特點:
- 無連接配接的:發資料前不需要建立連接配接。
- 不可靠:盡最大努力傳遞,即不保證可靠傳遞。
- 支援一對一,一對多,多對一和多對多的互動通信
- 占用資源少,發送資料快。
UDP流程: (本段來源)
UDP程式設計的用戶端一般步驟是:
- 1.建立 UDP socket套接字,用socket()函數。
- 2.用sendto()函數往指定的IP,位址發送資訊。
TCP程式設計的伺服器端一般步驟是:
- 建立 UDP socket套接字,用socket函數。
- 設定socket的屬性,用setsockopt()函數,(可選)
- 綁定包含 IP資訊,位址資訊的(IPv4)結構體。用bind()函數
- 循環接收消息,用recvfrom()函數
- 關閉socket套接字
TCP團夥和UDP團夥
軟體設計
ESP32的UDP詳細過程
ESP32的UDP Client接口介紹
連接配接函數:connect();
關閉socket函數:close();
擷取socket錯誤代碼:getsocketopt();
接收資料函數:recvfrom();
發送資料函數:sendto();
更多更詳細接口請參考官方指南。
ESP32的UDP總結
初始化wifi配置後,程式會根據WIFI的實時狀态,在回調函數中給出狀态傳回,是以隻需要在回調中進行相關操作,STA開始事件觸發UDP工作,上後就可以進行資料的廣播。
UDP建立任務編寫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | esp_err_t create_udp_client() { ESP_LOGI(TAG, "will connect gateway ssid : %s port:%d", UDP_ADRESS, UDP_PORT); //建立socket connect_socket = socket(AF_INET, SOCK_DGRAM, 0); /*參數和TCP不同*/ if (connect_socket < 0) { //列印報錯資訊 show_socket_error_reason("create client", connect_socket); //建立失敗後,關閉建立的socket,等待下次建立 close(connect_socket); return ESP_FAIL; } //配置連接配接伺服器資訊 client_addr.sin_family = AF_INET; client_addr.sin_port = htons(UDP_PORT); client_addr.sin_addr.s_addr = inet_addr(UDP_ADRESS); int len = 0; //長度 char databuff[1024] = "Hello Server,Please ack!!"; //緩存 //測試udp server,傳回發送成功的長度 len = sendto(connect_socket, databuff, 1024, 0, (struct sockaddr *) &client_addr, sizeof(client_addr)); if (len > 0) { ESP_LOGI(TAG, "Transfer data to %s:%u,ssucceed\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); } else { show_socket_error_reason("recv_data", connect_socket); close(connect_socket); return ESP_FAIL; } return ESP_OK; |
UDP接收任務代碼
void recv_data(void *pvParameters) char databuff[1024]; //緩存 while (1) //清空緩存 memset(databuff, 0x00, sizeof(databuff)); //讀取接收資料 len = recvfrom(connect_socket, databuff, sizeof(databuff), 0, (struct sockaddr *) &client_addr, &socklen); if (len > 0) { //列印接收到的數組 ESP_LOGI(TAG, "UDP Client recvData: %s", databuff); //接收資料回發 sendto(connect_socket, databuff, strlen(databuff), 0, (struct sockaddr *) &client_addr, sizeof(client_addr)); } else //列印錯誤資訊 show_socket_error_reason("UDP Client recv_data", connect_socket); break; close_socket(); vTaskDelete(NULL); |
測試流程和效果展示
測試流程
修改AP和STA的賬号密碼
修改UDP Port
使用手機或者電腦使用助手工具進行UDP廣播測試
效果展示
測試發送資料
收發小測
發送100次,看看不可靠的程度
UDP總結
底層重原理,應用中流程+接口。
此源碼沒有異常處理,自己移植需要适當修改,在接收任務中看傳回值,決定是否重新建立UDP Client,與TCP類似的操作。