一.走進(jìn)UDP協(xié)議:
UDP(UserDatagramProtocol)協(xié)議就是“用戶數(shù)據(jù)報(bào)協(xié)議”,它是一種無(wú)連接的協(xié)議,無(wú)連接主要是和TCP協(xié)議相比較的。我們知道當(dāng)利用TCP協(xié)議傳送數(shù)據(jù)的時(shí)候,首先必須先建立連接(也就是所謂的握手)才可以傳輸數(shù)據(jù)。而當(dāng)計(jì)算機(jī)利用UDP協(xié)議進(jìn)行數(shù)據(jù)傳輸?shù)臅r(shí)候,發(fā)送方只需要知道對(duì)方的IP地址和端口號(hào)就可以發(fā)送數(shù)據(jù),而并不需要進(jìn)行連接。當(dāng)然如果你非要進(jìn)行連接,通過(guò)VisualC#也是可以實(shí)現(xiàn)的,但前提是要確定連接的遠(yuǎn)程主機(jī)的端口號(hào)處于監(jiān)聽(tīng)狀態(tài),否則程序會(huì)出現(xiàn)不必要的錯(cuò)誤,但這是種畫(huà)蛇添足的做法,不僅丟失了UDP協(xié)議的無(wú)連接傳送數(shù)據(jù)的特點(diǎn)和優(yōu)點(diǎn),而且還給程序運(yùn)行帶來(lái)了不安定的因素。所以這種方法并不值得提倡。
由于UDP協(xié)議并不需要進(jìn)行確定的連接,所以編寫(xiě)基于UDP協(xié)議的應(yīng)用程序比起編寫(xiě)基于TCP協(xié)議的應(yīng)用程序要簡(jiǎn)單些(程序中可以不需要考慮連接和一些異常的捕獲工作)。但同時(shí)也給基于UDP協(xié)議編寫(xiě)的程序帶來(lái)了一個(gè)致命的缺點(diǎn),UDP由于不提供可靠數(shù)據(jù)的傳輸,當(dāng)計(jì)算機(jī)之間利用UDP協(xié)議傳送數(shù)據(jù)的時(shí)候,發(fā)送方只管發(fā)送數(shù)據(jù),而并不確認(rèn)數(shù)據(jù)是否被對(duì)方接收。這樣就會(huì)導(dǎo)致某些UDP協(xié)議數(shù)據(jù)包在傳送的過(guò)程中丟失,尤其網(wǎng)絡(luò)質(zhì)量不令人滿意的情況下,丟失數(shù)據(jù)包的現(xiàn)象會(huì)更嚴(yán)重。這就是為什么在網(wǎng)絡(luò)上傳輸重要數(shù)據(jù)不采用UDP協(xié)議的原因。
但是我們也不能因?yàn)檫@一個(gè)缺點(diǎn)就全面否定UDP協(xié)議,這是因?yàn)殡m然利用UDP協(xié)議來(lái)傳送安全性要求高的數(shù)據(jù)是不適合的,但對(duì)于那些不重要的數(shù)據(jù),或者即使丟失若干數(shù)據(jù)包也不影響整體性的數(shù)據(jù),如音頻數(shù)據(jù),視頻數(shù)據(jù)等,采用UDP協(xié)議就是一個(gè)非常不錯(cuò)的選擇。如目前網(wǎng)絡(luò)流行的很多即時(shí)聊天程序,如OICQ和ICQ等,采用的就是UDP協(xié)議。同時(shí)雖然UDP協(xié)議無(wú)法保證數(shù)據(jù)可靠性,但具有對(duì)網(wǎng)絡(luò)資源開(kāi)銷(xiāo)較小,數(shù)據(jù)處理速度快的優(yōu)點(diǎn),所以在有些對(duì)數(shù)據(jù)安全性要求不是很高的情況下,采用UDP協(xié)議也是一個(gè)非常不錯(cuò)的選擇。
總結(jié)一下上面的內(nèi)容,可見(jiàn)UDP是一種不面向連接的網(wǎng)絡(luò)協(xié)議,既有其優(yōu)點(diǎn),也有其不足,具體如下:
1.基于UDP協(xié)議的網(wǎng)絡(luò)應(yīng)用程序,實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單,并且基于UDP協(xié)議的網(wǎng)絡(luò)應(yīng)用程序在運(yùn)行時(shí),由于受到環(huán)境影響較小,所以不容易出錯(cuò)。
2.UDP協(xié)議占用網(wǎng)絡(luò)資源較少,數(shù)據(jù)處理較快,所以在網(wǎng)絡(luò)中傳送對(duì)安全性要求不是十分高數(shù)據(jù)時(shí),其優(yōu)點(diǎn)比較明顯。所謂對(duì)安全性要求不高的數(shù)據(jù),是指那些不重要的數(shù)據(jù),或者是即使丟失若干數(shù)據(jù),也不影響其整體的數(shù)據(jù),如音頻數(shù)據(jù)等。目前很多流行的網(wǎng)絡(luò)應(yīng)用程序都是基于UDP協(xié)議的,如OICQ、ICQ等。
3.由于其不是面向連接的網(wǎng)絡(luò)協(xié)議,其缺點(diǎn)也是非常明顯的,有些時(shí)候甚至是致命的。因?yàn)槭褂肬DP協(xié)議來(lái)傳送數(shù)據(jù),在數(shù)據(jù)發(fā)送后,在發(fā)送方并不確認(rèn)對(duì)方是否接收到。這樣就可能導(dǎo)致傳送的數(shù)據(jù)在網(wǎng)絡(luò)中丟失,尤其在網(wǎng)絡(luò)條件并不很好的情況下,丟失數(shù)據(jù)包的現(xiàn)象就更多。所以傳送重要數(shù)據(jù)一般不采用UDP協(xié)議。
二.簡(jiǎn)介VisualC#發(fā)送、接收UDP數(shù)據(jù)包使用的主要類及其用法:
用VisualC#實(shí)現(xiàn)UDP協(xié)議,最為常用,也是最為關(guān)鍵的類就是UdpClient,UdpClient位于命名空間System.Net.Sockets中,VisualC#發(fā)送、接收UDP數(shù)據(jù)包都是通過(guò)UdpClient類的。表01和表02是UdpClient類中常用方法和屬性及其簡(jiǎn)要說(shuō)明。
方法
說(shuō)明
Close
關(guān)閉 UDP 連接
Connect
建立與遠(yuǎn)程主機(jī)的連接
DropMulticastGroup
退出多路廣播組
JoinMulticastGroup
將 UdpClient 添加到多路廣播組
Receive
返回已由遠(yuǎn)程主機(jī)發(fā)送的 UDP 數(shù)據(jù)文報(bào)
Send
將 UDP 數(shù)據(jù)文報(bào)發(fā)送到遠(yuǎn)程主機(jī)
表01:UdpClient類中常用方法及其說(shuō)明。
屬性
說(shuō)明
Active
獲取或設(shè)置一個(gè)值,該值指示是否已建立了與遠(yuǎn)程主機(jī)的連接
Client
獲取或設(shè)置基礎(chǔ)網(wǎng)絡(luò)套接字
表02:UdpClient類中常用方法及其說(shuō)明。
1.Visual C#使用UdpClient類發(fā)送UDP數(shù)據(jù)包:
在具體使用中,一般分成二種情況:
(1). 知道遠(yuǎn)程計(jì)算機(jī)IP地址:
"Send"方法的調(diào)用語(yǔ)法如下:
|
參數(shù)說(shuō)明:
dgram 要發(fā)送的 UDP 數(shù)據(jù)文報(bào)(以字節(jié)數(shù)組表示)。
bytes 數(shù)據(jù)文報(bào)中的字節(jié)數(shù)。
endPoint 一個(gè) IPEndPoint,它表示要將數(shù)據(jù)文報(bào)發(fā)送到的主機(jī)和端口。
返回值 已發(fā)送的字節(jié)數(shù)。
下面使用UdpClient發(fā)送UDP數(shù)據(jù)包的具體的調(diào)用例子:
|
(2). 知道遠(yuǎn)程計(jì)算機(jī)名稱::
知道遠(yuǎn)程計(jì)算機(jī)名稱后,利用"Send"方法直接把UDP數(shù)據(jù)包發(fā)送到遠(yuǎn)程主機(jī)的指定端口號(hào)上了,這種調(diào)用方式也是最容易的,語(yǔ)法如下:
|
參數(shù)說(shuō)明:
dgram 要發(fā)送的 UDP 數(shù)據(jù)文報(bào)(以字節(jié)數(shù)組表示)。
bytes 數(shù)據(jù)文報(bào)中的字節(jié)數(shù)。
hostname 要連接到的遠(yuǎn)程主機(jī)的名稱。
port 要與其通訊的遠(yuǎn)程端口號(hào)。
返回值 已發(fā)送的字節(jié)數(shù)。
2.Visual C#使用UdpClient類接收UDP數(shù)據(jù)包:
接收UDP數(shù)據(jù)包使用的是UdpClient中的“Receive”方法。此方法的調(diào)用語(yǔ)法如下:
|
參數(shù)
remoteEP 是一個(gè) IPEndPoint類的實(shí)例,它表示網(wǎng)絡(luò)中發(fā)送此數(shù)據(jù)包的節(jié)點(diǎn)。
如果指定了遠(yuǎn)程計(jì)算機(jī)要發(fā)送到本地機(jī)的端口號(hào),也可以通過(guò)偵聽(tīng)本地端口號(hào)來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)的獲取,下面就是通過(guò)偵聽(tīng)本地端口號(hào)“8080”來(lái)獲取信息代碼:
|
三.Visual C#實(shí)現(xiàn)UDP協(xié)議之網(wǎng)絡(luò)對(duì)時(shí)系統(tǒng)的體系結(jié)構(gòu)及功能簡(jiǎn)介:
在局域網(wǎng)中有很多應(yīng)用軟件為了協(xié)同工作,需要保證客戶機(jī)上時(shí)間統(tǒng)一,而為了實(shí)現(xiàn)這一點(diǎn),通常的做法是客戶機(jī)從一個(gè)時(shí)間相對(duì)正確的服務(wù)器讀取時(shí)間,以此來(lái)校正本地時(shí)間。如經(jīng)常看到的GPS對(duì)時(shí)系統(tǒng)等。本節(jié)編寫(xiě)的局域網(wǎng)上對(duì)時(shí)系統(tǒng)的主要的功能是保證局域網(wǎng)上計(jì)算機(jī)時(shí)間、日期的統(tǒng)一。網(wǎng)絡(luò)對(duì)時(shí)程序是體系結(jié)構(gòu)分成服務(wù)器端程序和客戶端程序二個(gè)部分,具體的作法是:在同一個(gè)網(wǎng)段上,固定一臺(tái)計(jì)算機(jī)作為對(duì)時(shí)的服務(wù)器,在這個(gè)網(wǎng)段的所有計(jì)算機(jī)都可以讀取這臺(tái)服務(wù)器上的時(shí)間和日期,并依此服務(wù)器上的時(shí)間和日期為基準(zhǔn),來(lái)確定本地的時(shí)間和日期。在服務(wù)器端程序需要達(dá)到以下功能:
能夠接收局域網(wǎng)中任一臺(tái)客戶機(jī)的請(qǐng)求
記錄請(qǐng)求客戶機(jī)的計(jì)算機(jī)名稱,和請(qǐng)求時(shí)間
準(zhǔn)確發(fā)送服務(wù)器端的時(shí)間和日期
端程序要達(dá)到以下功能:
能夠設(shè)定服務(wù)器的主機(jī)或者IP地址
能夠接收服務(wù)器端發(fā)送的時(shí)間、日期信息
能夠以接收的服務(wù)器端時(shí)間、日期為基準(zhǔn),校正本地時(shí)間
因此在具體用Visual C#實(shí)現(xiàn)網(wǎng)絡(luò)對(duì)時(shí)系統(tǒng)時(shí)就包括二個(gè)部分:服務(wù)器端程序和客戶端程序。下面首先介紹Visual C#實(shí)現(xiàn)網(wǎng)絡(luò)對(duì)時(shí)系統(tǒng)中服務(wù)器端程序的具體步驟。
四.Visual C#實(shí)現(xiàn)網(wǎng)絡(luò)對(duì)時(shí)系統(tǒng)之服務(wù)器端程序的具體步驟:
服務(wù)器端程序比客戶端程序相對(duì)要簡(jiǎn)單,主要因是服務(wù)器端程序的工作比較簡(jiǎn)單,就
是接收客戶端的對(duì)時(shí)請(qǐng)求、發(fā)送服務(wù)器端的時(shí)間數(shù)據(jù)。而于客戶端不僅要傳送和接收數(shù)據(jù),還要把服務(wù)器端的時(shí)間提取出來(lái),并以此來(lái)修改本地計(jì)算機(jī)的時(shí)間、日期。下面是用Visual C#實(shí)現(xiàn)網(wǎng)絡(luò)對(duì)時(shí)系統(tǒng)之服務(wù)器端程序的具體步驟步驟。
1.啟動(dòng)Visual Studio .Net。
2.選擇菜單【文件】|【新建】|【項(xiàng)目】后,彈出【新建項(xiàng)目】對(duì)話框。
3.將【項(xiàng)目類型】設(shè)置為【Visual C#項(xiàng)目】。
4.將【模板】設(shè)置為【W(wǎng)indows應(yīng)用程序】。
5.在【名稱】文本框中輸入【UDP對(duì)時(shí)服務(wù)器端】。
6.在【位置】的文本框中輸入【E:VS.NET項(xiàng)目】,然后單擊【確定】按鈕
7.在【解決方案資源管理器】窗口中,雙擊Form1.cs文件,進(jìn)入Form1.cs文件的編輯界面。
8.在Form1.cs文件的開(kāi)頭,用下列導(dǎo)入命名空間代碼替代系統(tǒng)缺省的導(dǎo)入命名空間代碼。
|
9.切換到【Form1.cs(設(shè)計(jì))】窗口,并從【工具箱】中的【W(wǎng)indows窗體組件】中往窗體中拖入下列組件,并執(zhí)行相應(yīng)操作:
一個(gè)Label組件,顯示對(duì)時(shí)服務(wù)器正在運(yùn)行信息
一個(gè)ListBox組件,名稱為listBox1,用以顯示客戶端和服務(wù)器端交流的日志
一個(gè)Button組件,名稱為button1,并在其拖入窗體后,雙擊,則系統(tǒng)會(huì)在Form1.cs文件中產(chǎn)生其Click事件對(duì)應(yīng)的處理代碼。
10.在【解決方案資源管理器】窗口中,雙擊Form1.cs文件,進(jìn)入Form1.cs文件的編輯界面。在Form1.cs中的class代碼區(qū)添加下列代碼,下列代碼是定義程序中使用的全局變量和創(chuàng)建全局使用的實(shí)例:
|
11.以下面代碼替代系統(tǒng)產(chǎn)生的InitializeComponent過(guò)程。
|
至此,【UDP對(duì)時(shí)服務(wù)器端】項(xiàng)目的界面設(shè)計(jì)和功能實(shí)現(xiàn)的前期工作就完成了,設(shè)計(jì)界面如圖01所示:

12.在Form1.cs文件中的InitializeComponent過(guò)程的后面添加下面代碼,下列代碼是定義過(guò)程“start_server”。此過(guò)程的功能是獲取客戶端對(duì)時(shí)請(qǐng)求數(shù)據(jù),并向客戶端發(fā)送服務(wù)器當(dāng)前時(shí)間和日期。
|
請(qǐng)注意:上述代碼中約定客戶機(jī)程序發(fā)送對(duì)時(shí)請(qǐng)求信息到服務(wù)器的8080端口號(hào)。服務(wù)器端程序接收發(fā)送到本地8080端口號(hào)的數(shù)據(jù)就完成了數(shù)據(jù)接收。為了能夠讓服務(wù)器端程序知道是那臺(tái)客戶機(jī)提出請(qǐng)求和要把對(duì)時(shí)信息發(fā)送到客戶機(jī)的那個(gè)端口號(hào)上,客戶端程序?qū)Πl(fā)送的對(duì)時(shí)請(qǐng)求信息進(jìn)行了設(shè)計(jì)。客戶端的對(duì)時(shí)請(qǐng)求信息結(jié)構(gòu)為:
計(jì)算機(jī)名稱 + / + 客戶機(jī)接收信息端口號(hào)
這樣如果客戶端計(jì)算機(jī)名稱為:majinhu,接收服務(wù)器端時(shí)間數(shù)據(jù)的端口號(hào)是8080,則客戶端程序發(fā)送的對(duì)時(shí)請(qǐng)求數(shù)據(jù)就為:majinhu/8080。
服務(wù)器端程序在接收到客戶端對(duì)時(shí)請(qǐng)求數(shù)據(jù),并進(jìn)行分析后,就能夠通過(guò)UdpClient類的Send方法準(zhǔn)確的把服務(wù)器端當(dāng)前的時(shí)間和日期發(fā)送到客戶端指定的端口號(hào)上。這樣客戶端程序通過(guò)讀取指定的端口號(hào),就能夠獲得服務(wù)器端當(dāng)前的時(shí)間和日期,從而以此來(lái)修正客戶端的時(shí)間和日期了。
13.在“start_server”過(guò)程之后面添加下面代碼,下列代碼是定義“run”過(guò)程。“run”過(guò)程的作用是創(chuàng)建一個(gè)線程實(shí)例,并以“start_server”過(guò)程來(lái)初始化線程實(shí)例。之所以采用線程是因?yàn)榉?wù)器端程序需要不間斷讀取發(fā)送到8080端口號(hào),并且Receive方法是一個(gè)阻塞式方法。采用線程就是為了保證服務(wù)器端程序正常運(yùn)行:
|
14.在Form1.cs中的Main函數(shù)之后添加下列代碼,下列代碼是定義“Form1_Load”事件,在此事件中將調(diào)用“run”過(guò)程,這樣當(dāng)服務(wù)器端程序運(yùn)行后,就啟動(dòng)網(wǎng)絡(luò)對(duì)時(shí)服務(wù):
|
15.在Form1.cs文件中的“Form1_Load”事件之后,添加下列代碼,下列代碼是定義button1的“Click”事件,此事件的作用是清除服務(wù)器端程序顯示的日志信息:
|
16.用下列代碼替換Form1.cs中的Dispose方法。下列代碼的功能是手動(dòng)收集程序中使用的資源:
|
至此,在上述步驟都正確完成,【UDP對(duì)時(shí)服務(wù)器端】項(xiàng)目的全部工作就完成了。圖02【UDP對(duì)時(shí)服務(wù)器端】運(yùn)行后的界面,在日志信息中記錄了對(duì)時(shí)請(qǐng)求客戶機(jī)的名稱,發(fā)送對(duì)時(shí)數(shù)據(jù)的端口號(hào)以及客戶端請(qǐng)求的時(shí)間:



