LwIP應用開發筆記之二:LwIP無操做系統UDP服務器

   前面咱們已經完成了LwIP協議棧基於邏輯的基本移植,在這一節咱們將以RAW API來實現UDP服務器。算法

1UDP協議簡述安全

  UDP協議全稱是用戶數據報協議,在網絡中它與TCP協議同樣用於處理數據包,是一種無鏈接的協議。在OSI模型中,處於傳輸層,是IP協議的上層協議。UDP有不提供數據包分組、組裝和不能對數據包進行排序的缺點,也就是說,當報文發送以後,是沒法得知其是否安全完整到達的。服務器

  UDP協議的主要做用是將網絡數據流量壓縮成數據包的形式。一個典型的數據包就是一個二進制數據的傳輸單位。每個數據包的前8個字節用來包含報頭信息,剩餘字節則用來包含具體的傳輸數據。網絡

  UDP報頭由4個域組成,其中每一個域各佔用2個字節,具體以下:源端口號、目標端口號、數據報長度、校驗值。其數據結構以下:數據結構

 

  UDP協議使用端口號爲不一樣的應用保留其各自的數據傳輸通道。UDP和TCP協議正是採用這一機制實現對同一時刻內多項應用同時發送和接收數據的支持。數據發送一方(能夠是客戶端或服務器端)將UDP數據包經過源端口發送出去,而數據接收一方則經過目標端口接收數據。有的網絡應用只能使用預先爲其預留或註冊的靜態端口;而另一些網絡應用則可使用未被註冊的動態端口。由於UDP報頭使用兩個字節存放端口號,因此端口號的有效範圍是從0到65535。通常來講,大於49151的端口號都表明動態端口。函數

  數據報的長度是指包括報頭和數據部分在內的總字節數。由於報頭的長度是固定的,因此該域主要被用來計算可變長度的數據部分。數據報的最大長度根據操做環境的不一樣而各異。從理論上說,包含報頭在內的數據報的最大長度爲65535字節。不過,一些實際應用每每會限制數據報的大小,有時會下降到8192字節。測試

  UDP協議使用報頭中的校驗值來保證數據的安全。校驗值首先在數據發送方經過特殊的算法計算得出,在傳遞到接收方以後,還須要再從新計算。若是某個數據報在傳輸過程當中被第三方篡改或者因爲線路噪音等緣由受到損壞,發送和接收方的校驗計算值將不會相符,由此UDP協議能夠檢測是否出錯。spa

2UDP服務器設計設計

  前面咱們簡要的介紹了UDP協議及其數據報,接下來咱們將考慮怎麼實現基於UDP協議的服務器。指針

  首先,咱們來看一看與UDP相關的API函數,並對它們做一個初步的介紹,應爲咱們須要使用它們來實現咱們的應用。函數及說明以下:

 

  瞭解了這些函數,咱們如今考慮其實現過程。對於UDP服務器端來講,實現相對簡潔。其實現步驟以下:

  首先,生成一個新的UDP控制塊。

  接着,綁定UDP控制塊到任意IP地址及制定端口。

  最後,爲UDP控制塊註冊數據處理回調函數,這裏須要說明一下,這就是RAW AIP的回調函數。根據你要實現的功能不一樣複雜程度徹底不同。咱們因爲要實現一個迴環服務器,因此相對簡單。只須要將收到的信息,以咱們想要的方式發送回客戶端就能夠了。

  爲了很好的實現UDP服務器,還有一個問題須要設計好,就是咱們前面咱們曾提到的端口。咱們都知道TCP/IP協議族包括有不少的協議,那通信到底是針對哪個協議發生的呢?所謂兩臺機器間的通信,其實是主機上的應用進程間的通信,端口號就是爲了最終實現主機上應用進程的通信。咱們常見且會在後續使用到的協議端口以下:

 

  爲了使用方便咱們將這些端口定義爲宏,並存儲到一個專門的文件中。在這裏咱們本次實現UDP服務器也須要制定一個端口,其實支持UDP的端口都沒問題,但爲了方便描述咱們制定其爲迴環顯示端口。

3UDP服務器實現

  咱們瞭解了其實現的基本過程,其實並不複雜。事實上,回調函數的內容纔是咱們真正須要考慮的東西。咱們將其實現分爲兩個部分:一是UDP服務器的初始化部分;二是UDP服務器功能部分,也就是回調函數所執行的內容。

  首先實現UDP服務器的初始化部分。初始化部分定義一個新的UDP控制塊,並將其綁定到任意IP地址及指定端口。而後註冊數據處理回調函數。

 1 /* UDP初始化配置 */
 2 void UDP_Server_Initialization(void)
 3 {
 4   static char * recv_arg="We recieved a UDP data\n";
 5   struct udp_pcb *upcb;
 6  
 7   /* 生成一個新的UDP控制塊 */
 8   upcb = udp_new();
 9   
10   /* 綁定upcb塊到任意IP地址及指定端口*/
11   udp_bind(upcb, IP_ADDR_ANY, UDP_ECHO_SERVER_PORT);
12  
13   /* 爲upcb指定數據處理回調函數 */
14   udp_recv(upcb,UDPServerCallback,(void *)recv_arg);
15 }

  關於爲何要將本地IP綁定到任意IP呢?這是由於UDP服務器收到數據包後,LwIP會先判斷其數據包的目的IP和端口是否和本地註冊的PCB控制塊綁定的本地的IP和本地端口號是否匹配。因此咱們綁定PCB控制塊本地IP設爲IP_ADDR_ANY時,只要收到的數據包的目的IP非廣播地址,端口號匹配,那麼均認爲數據包的目的IP和端口是與本地註冊的PCB控制塊綁定的本地IP和端口號相匹配的。省去了本身構造本地IP的過程。

  初始化完畢後,註冊了數據處理回調函數。接下來須要實現回調函數的內容。回調函數主要實現對數據的處理,這取決於本身的需求。在這裏咱們在接收到UDP客戶端數據包後,不對其做什麼處理,由於這一數據原本無心義,咱們對任何的客戶端請求給予固定的回覆。

 1 /* 定義UDP服務器數據處理回調函數 */
 2 static void UDPServerCallback(void *arg,struct udp_pcb *upcb,struct pbuf *revBuf,const ip_addr_t *addr,u16_t port)
 3 {
 4   struct pbuf *sendBuf = NULL;
 5   const char* reply = "This is reply!\n";
 6  
 7   pbuf_free(revBuf);
 8   
 9   sendBuf = pbuf_alloc(PBUF_TRANSPORT, strlen(reply)+1, PBUF_RAM);
10   if(!sendBuf)
11   {
12     return;
13   }
14  
15   memset(sendBuf->payload,0,sendBuf->len);
16   memcpy(sendBuf->payload, reply, strlen(reply));
17   udp_sendto(upcb, sendBuf, addr, port);
18   pbuf_free(sendBuf);
19 }

  對於這個回調函數,它實際是賦給一個函數指針,因此雖然它的內容和名稱能夠隨意,但其格式是有要求的:void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port)

4、結論

  至此,咱們完成了簡單的UDP服務器,在這裏咱們使用客戶端來測試一下這個UDP服務器,測試結果以下:

 

  這裏只測試了一個客戶端兩屆服務器的狀況,其實鏈接多個客戶端的狀況也是沒問題的。以下:

 

  佷顯然,若是咱們但願實現更復雜的UDP服務器,咱們只須要將咱們想實現的功能作到回調函數中就能夠了。

歡迎關注:

 

相關文章
相關標籤/搜索