前一節(jié)我們實現(xiàn)了基于RAW API的UDP服務器,在接下來,我們進一步利用RAW API實現(xiàn)UDP客戶端。
1 、 UDP****協(xié)議簡述
UDP協(xié)議全稱是用戶數(shù)據(jù)報協(xié)議,在網(wǎng)絡中它與TCP協(xié)議一樣用于處理數(shù)據(jù)包,是一種無連接的協(xié)議。在OSI模型中,處于傳輸層,是IP協(xié)議的上層協(xié)議。UDP有不提供數(shù)據(jù)包分組、組裝和不能對數(shù)據(jù)包進行排序的缺點,也就是說,當報文發(fā)送之后,是無法得知其是否安全完整到達的。
UDP協(xié)議的主要作用是將網(wǎng)絡數(shù)據(jù)流量壓縮成數(shù)據(jù)包的形式。一個典型的數(shù)據(jù)包就是一個二進制數(shù)據(jù)的傳輸單位。每一個數(shù)據(jù)包的前8個字節(jié)用來包含報頭信息,剩余字節(jié)則用來包含具體的傳輸數(shù)據(jù)。
UDP報頭由4個域組成,其中每個域各占用2個字節(jié),具體如下:源端口號、目標端口號、數(shù)據(jù)報長度、校驗值。其數(shù)據(jù)結構如下:
UDP協(xié)議使用端口號為不同的應用保留其各自的數(shù)據(jù)傳輸通道。UDP和TCP協(xié)議正是采用這一機制實現(xiàn)對同一時刻內多項應用同時發(fā)送和接收數(shù)據(jù)的支持。數(shù)據(jù)發(fā)送一方(可以是客戶端或服務器端)將UDP數(shù)據(jù)包通過源端口發(fā)送出去,而數(shù)據(jù)接收一方則通過目標端口接收數(shù)據(jù)。有的網(wǎng)絡應用只能使用預先為其預留或注冊的靜態(tài)端口;而另外一些網(wǎng)絡應用則可以使用未被注冊的動態(tài)端口。因為UDP報頭使用兩個字節(jié)存放端口號,所以端口號的有效范圍是從0到65535。一般來說,大于49151的端口號都代表動態(tài)端口。
數(shù)據(jù)報的長度是指包括報頭和數(shù)據(jù)部分在內的總字節(jié)數(shù)。因為報頭的長度是固定的,所以該域主要被用來計算可變長度的數(shù)據(jù)部分。數(shù)據(jù)報的最大長度根據(jù)操作環(huán)境的不同而各異。從理論上說,包含報頭在內的數(shù)據(jù)報的最大長度為65535字節(jié)。不過,一些實際應用往往會限制數(shù)據(jù)報的大小,有時會降低到8192字節(jié)。
UDP協(xié)議使用報頭中的校驗值來保證數(shù)據(jù)的安全。校驗值首先在數(shù)據(jù)發(fā)送方通過特殊的算法計算得出,在傳遞到接收方之后,還需要再重新計算。如果某個數(shù)據(jù)報在傳輸過程中被第三方篡改或者由于線路噪音等原因受到損壞,發(fā)送和接收方的校驗計算值將不會相符,由此UDP協(xié)議可以檢測是否出錯。
2 、 UDP****客戶端設計
前面我們簡要的介紹了UDP協(xié)議及其數(shù)據(jù)報,接下來我們將考慮怎么實現(xiàn)基于UDP協(xié)議的客戶端。
首先,我們來看一看與UDP相關的API函數(shù),并對它們作一個初步的介紹,應為我們需要使用它們來實現(xiàn)我們的應用。函數(shù)及說明如下:
我們已經(jīng)了解了UDP服務器的實現(xiàn)步驟,接下來我們說明一下UDP客戶端的實現(xiàn)步驟。
首先,依然是創(chuàng)建一個新的UDP控制塊。
接下來,建立與服務器的連接,配置包括服務器的地址、端口等信息。
接下來,如果連接無問題,則注冊客戶端回調函數(shù)。與服務器端的實現(xiàn)一樣,其復雜程度與需要實現(xiàn)的功能相關。我們只是實現(xiàn)一個簡單UDP客戶端,所以我們向服務器發(fā)送固定的信息,收到回復后繼續(xù)發(fā)送對應的信息。
最后,由于客戶端是對話的發(fā)起方,所以在注冊完回調函數(shù)后,客戶端要發(fā)起首次對話。
3 、 UDP****客戶端實現(xiàn)
對UDP服務器端的實現(xiàn),我們依然將器分為兩方面內容:一是,UDP客戶端的初始化配置部分;二是,UDP客戶端的具體實現(xiàn)內容,也就是回調函數(shù)的內容。
首先實現(xiàn)UDP客戶端的初始化配置部分。定義新的UDP控制塊,連接到指定服務器的地址及端口,同樣由于我們的驗證比較簡單我們采用回環(huán)服務器端口。然后注冊回調函數(shù),發(fā)起客戶端首次通訊。具體代碼如下:
1 /* UDP客戶端初始化配置 */
2 void UDP_Client_Initialization(void)
3 {
4 ip_addr_t DestIPaddr;
5 err_t err;
6 struct udp_pcb *upcb;
7 char data[]="This is a Client.";
8
9 /* 設置服務器端的IP地址 */
10 IP4_ADDR( &DestIPaddr,udpServerIP[0],udpServerIP[1],udpServerIP[2],udpServerIP[3]);
11
12 /* 創(chuàng)建一個新的UDP控制塊 */
13 upcb = udp_new();
14
15 if (upcb!=NULL)
16 {
17 /* 服務器端地址、端口配置 */
18 err= udp_connect(upcb, &DestIPaddr, UDP_ECHO_SERVER_PORT);
19
20 if (err == ERR_OK)
21 {
22 /* 注冊回調函數(shù) */
23 udp_recv(upcb, UDPClientCallback, NULL);
24 /**數(shù)據(jù)發(fā)送,第一次連接時客戶端發(fā)送數(shù)據(jù)至服務器端,發(fā)送函數(shù)中會遍歷查找源IP地址的配置,如果源IP地址未配置,則數(shù)據(jù)發(fā)送失敗。該處出現(xiàn)的問題在后面總結中提到了**/
25 UdpClientSendPacket(upcb,data);
26 }
27 }
28 }
其次實現(xiàn)UDP客戶端的具體實現(xiàn)內容。由于我們實現(xiàn)的簡單的響應客戶端,所以我們只是給服務器回復相同的內容。
1 /* 定義UDP客戶端數(shù)據(jù)處理回調函數(shù) */
2 static void UDPClientCallback(void *arg,struct udp_pcb *upcb,struct pbuf *p,const ip_addr_t *addr,u16_t port)
3 {
4 udp_send(upcb, p); //數(shù)據(jù)回顯
5
6 pbuf_free(p);
7 }
8
9 /* 客戶端數(shù)據(jù)發(fā)送函數(shù) */
10 void UdpClientSendPacket(struct udp_pcb *upcb,char* data)
11 {
12 struct pbuf *p;
13
14 /* 分配內存空間 */
15 p = pbuf_alloc(PBUF_TRANSPORT,strlen((char*)data), PBUF_POOL);
16
17 if (p != NULL)
18 {
19
20 /* 復制數(shù)據(jù)到pbuf */
21 pbuf_take(p, (char*)data, strlen((char*)data));
22
23 /* 發(fā)送數(shù)據(jù) */
24 udp_send(upcb, p); //發(fā)送數(shù)據(jù)
25
26 /* 釋放pbuf */
27 pbuf_free(p);
28 }
29 }
當然,如果我們不想人云亦云的回復服務器,則可以編輯我們自己的數(shù)據(jù)包然后發(fā)送回去。所以我們想要實現(xiàn)復雜的應用時,只需要重新編寫合適的回調函數(shù)就可以了!
4 、結論
我們完成了簡單的,基于RAW API的UDP客戶端,其本身并不復雜。同樣的我們使用網(wǎng)絡軟件測試其功能,我們在電腦上建立一個服務器端,然后通過我們這個客戶端去連接它。能夠進行連接并發(fā)送接受數(shù)據(jù),說明我們這個客戶端的設計是符合要求的。
至此我們完成了UDP客戶端及服務器的實現(xiàn),后續(xù)我們將在次基礎上實現(xiàn)更為復雜的應用。
-
API
+關注
關注
2文章
1472瀏覽量
61749 -
UDP
+關注
關注
0文章
322瀏覽量
33849 -
客戶端
+關注
關注
1文章
289瀏覽量
16640 -
RAW
+關注
關注
0文章
21瀏覽量
3785
發(fā)布評論請先 登錄
相關推薦
評論