基于UDP的服务器端和客户端
前面的文章中我們給出了幾個 TCP 的例子,對于 UDP 而言,只要能理解前面的內容,實現并非難事。
UDP中的服務器端和客戶端沒有連接
UDP 不像 TCP,無需在連接狀態下交換數據,因此基于 UDP 的服務器端和客戶端也無需經過連接過程。也就是說,不必調用 listen() 和 accept() 函數。UDP 中只有創建套接字的過程和數據交換的過程。
UDP服務器端和客戶端均只需1個套接字
TCP 中,套接字是一對一的關系。如要向 10 個客戶端提供服務,那么除了負責監聽的套接字外,還需要創建 10 套接字。但在 UDP 中,不管是服務器端還是客戶端都只需要 1 個套接字。之前解釋 UDP 原理的時候舉了郵寄包裹的例子,負責郵寄包裹的快遞公司可以比喻為 UDP 套接字,只要有 1 個快遞公司,就可以通過它向任意地址郵寄包裹。同樣,只需 1 個 UDP 套接字就可以向任意主機傳送數據。
基于UDP的接收和發送函數
創建好 TCP 套接字后,傳輸數據時無需再添加地址信息,因為 TCP 套接字將保持與對方套接字的連接。換言之,TCP 套接字知道目標地址信息。但 UDP 套接字不會保持連接狀態,每次傳輸數據都要添加目標地址信息,這相當于在郵寄包裹前填寫收件人地址。
發送數據使用 sendto() 函數:
Linux 和 Windows 下的 sendto() 函數類似,下面是詳細參數說明:
- sock:用于傳輸 UDP 數據的套接字;
- buf:保存待傳輸數據的緩沖區地址;
- nbytes:帶傳輸數據的長度(以字節計);
- flags:可選項參數,若沒有可傳遞 0;
- to:存有目標地址信息的 sockaddr 結構體變量的地址;
- addrlen:傳遞給參數 to 的地址值結構體變量的長度。
UDP 發送函數 sendto() 與TCP發送函數 write()/send() 的最大區別在于,sendto() 函數需要向他傳遞目標地址信息。
接收數據使用 recvfrom() 函數:
由于 UDP 數據的發送端不定,所以 recvfrom() 函數定義為可接收發送端信息的形式,具體參數如下:
- sock:用于接收 UDP 數據的套接字;
- buf:保存接收數據的緩沖區地址;
- nbytes:可接收的最大字節數(不能超過 buf 緩沖區的大小);
- flags:可選項參數,若沒有可傳遞 0;
- from:存有發送端地址信息的 sockaddr 結構體變量的地址;
- addrlen:保存參數 from 的結構體變量長度的變量地址值。
基于UDP的回聲服務器端/客戶端
下面結合之前的內容實現回聲客戶端。需要注意的是,UDP 不同于 TCP,不存在請求連接和受理過程,因此在某種意義上無法明確區分服務器端和客戶端,只是因為其提供服務而稱為服務器端,希望各位讀者不要誤解。
下面給出 Windows 下的代碼,Linux 與此類似,不再贅述。
服務器端 server.cpp:
代碼說明:
1) 第 12 行代碼在創建套接字時,向 socket() 第二個參數傳遞?SOCK_DGRAM,以指明使用 UDP 協議。
2) 第 18 行代碼中使用htonl(INADDR_ANY)來自動獲取 IP 地址。
利用常數 INADDR_ANY 自動獲取 IP 地址有一個明顯的好處,就是當軟件安裝到其他服務器或者服務器 IP 地址改變時,不用再更改源碼重新編譯,也不用在啟動軟件時手動輸入。而且,如果一臺計算機中已分配多個 IP 地址(例如路由器),那么只要端口號一致,就可以從不同的 IP 地址接收數據。所以,服務器中優先考慮使用 INADDR_ANY;而客戶端中除非帶有一部分服務器功能,否則不會采用。
客戶端 client.cpp:
先運行 server,再運行 client,client 輸出結果為:
Input a string:?C語言中文網
Message form server: C語言中文網
Input a string: c.biancheng.net Founded in 2012
Message form server: c.biancheng.net Founded in 2012
Input a string:
從代碼中可以看出,server.cpp 中沒有使用 listen() 函數,client.cpp 中也沒有使用 connect() 函數,因為 UDP 不需要連接。
總結
以上是生活随笔為你收集整理的基于UDP的服务器端和客户端的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 再谈UDP和TCP
- 下一篇: 递推算法之滚动数组思维方式