LwIP應用開發筆記之十一:LwIP帶操作系統UDP服務器


  我們已經實現了在FreeRTOS系統上的LwIP的移植工作,但只是簡單的在系統平台上跑了起來。我們還希望能做更多的事情,這一節我們就在FreeRTOS系統上實現基於LwIP的UDP服務器。

1、UDP協議簡述

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

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

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

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

  數據報的長度是指包括報頭和數據部分在內的總字節數。因為報頭的長度是固定的,所以該域主要被用來計算可變長度的數據部分。數據報的最大長度根據操作環境的不同而各異。從理論上說,包含報頭在內的數據報的最大長度為65535字節。不過,一些實際應用往往會限制數據報的大小,有時會降低到8192字節。

  UDP協議使用報頭中的校驗值來保證數據的安全。校驗值首先在數據發送方通過特殊的算法計算得出,在傳遞到接收方之后,還需要再重新計算。如果某個數據報在傳輸過程中被第三方篡改或者由於線路噪音等原因受到損壞,發送和接收方的校驗計算值將不會相符,由此UDP協議可以檢測是否出錯。

2、帶系統UDP服務器的設計

  關於UDP服務器,我們以前在裸機狀態下,使用RAW/CallBack API函數實現過。在這里我們將基於操作系統來實現UDP服務器,在此我們需要使用netconn API函數實現。

2.1、netconn API

  在帶操作系統的LwIP應用中,應用程序需要使用netconn API函數來實現相關的應用,接下來我們了解一下netconn API函數。

(1)、公用部分函數

  其中即可用於TCP也可用於UDP的公共netconn API函數如下:

序號 函數 描述
1 netconn_new() 創建一個新連接
2 netconn_peer() 獲取遠程IP地址和端口
3 netconn_addr() 獲取本地IP地址和端口
4 netconn_set_ipv6only() 設置netconn調用的IPv6狀態
5 netconn_get_ipv6only() 獲取netconn調用的IPv6狀態
6 netconn_delete() 刪除現有連接
7 netconn_bind() 綁定到本地端口/ ip的連接
8 netconn_connect() 連接到遠程端口/ ip的連接
9 netconn_recv() 從netconn接收數據
10 netconn_gethostbyname_addrtype () 執行DNS查詢,只返回一個IP地址

(2)、用於TCP的函數

  對於TCP連接來說,還包括如下的netconn API函數:

序號 函數 描述
1 netconn_listen() 將TCP連接設置為偵聽模式
2 netconn_write() 在連接的TCP netconn上發送數據
3 netconn_listen_with_backlog () 將TCP netconn設置為偵聽模式
4 netconn_accept() 接受偵聽TCP連接上的傳入連接
5 netconn_recv_tcp_pbuf () 從TCP netconn接收數據(以pbuf的形式)
6 netconn_write_partly () 通過TCP netconn發送數據
7 netconn_close() 關閉TCP netconn而不刪除它
8 netconn_shutdown () 關閉TCP netconn的一端或兩端(不刪除它)

(3)、用於UDP的函數

  對於UDP連接來說,還包括如下的netconn API函數:

序號 函數 描述
1 netconn_disconnect() 斷開與遠程端口/ ip的連接
2 netconn_sendto() 將數據發送到指定的遠程端口/ ip(不適用於TCP)
3 netconn_send() 將數據發送到當前連接的遠程端口/ ip(不適用於TCP)
4 netconn_join_leave_group() 基本的IGMP多播支持

2.2、UDP服務器的流程

  在RAW API實現UDP服務器時,我們使用回調函數,當接受到數據報文時,回調函數會被調用。在有操作系統的情況下,我們肯定是實現多線程,所以我們將UDP服務器設定為一個任務來執行。在這個任務中我們將按如下流程來實現UDP服務器。

  從上圖中我們與無操作系統時的操作很類似。創建控制塊、綁定端口等是一樣的。但在內部接收和發送報文的方式卻是有區別的。

  至於UDP服務器最終實現了哪些功能,需要我們根據實際需要在處理並返回信息階段實施。功能可以很復雜也可以很簡單,在這里我們就是實現一個簡單的回環服務器。

3、帶系統UDP服務器的實現

  我們已經明白了UDP服務器在使用netconn API的實現方式及流程。接下來我們就來實現它。我們通過兩個函數來實現:一是初始化任務,即創建相應的任務;二是實現這個任務函數,也就是我們的UDP服務器。

  先實現任務的創建。這個函數很簡單,因為在移植LwIP協議棧時,要求在sys_arch.c文件中實現一個名為sys_thread_new的任務創建函數,而我們已經實現了這個任務創建函數,所以我們直接調用它就好了。

/* UDP初始化配置 */
void UDP_Server_Initialization(void)
{
 sys_thread_new("udpserver_thread", UDPServerThread, NULL, DEFAULT_THREAD_STACKSIZE,UDPECHO_THREAD_PRIO );
}

  接下來,我們看看UDP服務器任務函數的實現,根據上一節我們給出的流程,實現如下:

/* 定義UDP服務器數據處理進程 */
static void UDPServerThread(void *arg)
{
 err_t err, recv_err;
 static struct netconn *conn;
 static struct netbuf *buf;
static ip_addr_t *addr;
static unsigned short port;
 
 LWIP_UNUSED_ARG(arg);
 
 conn = netconn_new(NETCONN_UDP);
 if (conn!= NULL)
 {
  err = netconn_bind(conn, IP_ADDR_ANY,UDP_ECHO_SERVER_PORT);
  if (err == ERR_OK)
  {
   while (1) 
   {
     recv_err = netconn_recv(conn, &buf);
   
     if (recv_err == ERR_OK) 
     {
      addr = netbuf_fromaddr(buf);
      port = netbuf_fromport(buf);
      netconn_connect(conn, addr, port);
      buf->addr.addr = 0;
      netconn_send(conn,buf);
      netbuf_delete(buf);
     }
   }
  }
  else
  {
   netconn_delete(conn);
  }
 }
}

  對於UDP連接來說,netconn_connect函數的調用只是簡單的設置UDP控制塊中的remote_ip和remote_port字段。其實在這里不使用該函數也是沒問題的,因為buf中已經包含了相關的信息。

4、帶系統UDP服務器總結

  我們實現了一個簡單的UDP服務器應用,其實帶有操作系統時只是在軟件編寫方面采用的形式不一樣。從外界看來,依然是一個UDP服務器,與有無操作系統無關。所以我們的測試方法也是一樣的,與我們預期的結果也是一樣的。

歡迎關注:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM