引言:
成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供蒙陰網(wǎng)站建設(shè)、蒙陰做網(wǎng)站、蒙陰網(wǎng)站設(shè)計、蒙陰網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、蒙陰企業(yè)網(wǎng)站模板建站服務(wù),十年蒙陰做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
前一個專題簡單介紹了TCP編程的一些知識,UDP與TCP地位相當(dāng)?shù)牧硪粋€傳輸層協(xié)議,它也是當(dāng)下流行的很多主流網(wǎng)絡(luò)應(yīng)用(例如QQ、MSN和Skype等一些即時通信軟件傳輸層都是應(yīng)用UDP協(xié)議的)底層的傳輸基礎(chǔ),所以在本專題中就簡單介紹下UDP的工作原理和UDP編程的只是,希望可以對剛接觸網(wǎng)絡(luò)編程的朋友起到入門的作用。
一、UDP介紹
UDP和TCP都是構(gòu)建在IP層之上傳輸層的協(xié)議,但UDP是一種簡單、面向數(shù)據(jù)報(Sock_Dgram)的無連接協(xié)議,提供的是不一定可靠的傳輸服務(wù)。
然而TCP是一種面向連接、可靠的,面向字節(jié)流(Sock_Stream)的傳輸協(xié)議,對于“無連接”是指在正式通信前不必與對方先建立連接,不管對方狀態(tài)如何都可以直接發(fā)送過去(就如QQ中通過QQ號查看好友后發(fā)送添加好友請求,此間不需要考慮對方的狀態(tài)如何,都照樣發(fā)送請求)。從UDP和TCP的定義中就可以看出它們兩者的區(qū)別了,(1)UDP的可靠性不如TCP,因為TCP傳輸前要首先建立連接,這樣就增加了TCP傳輸?shù)目煽啃?,所以UDP也被稱為不可靠的傳輸協(xié)議,關(guān)于TCP的介紹可以看我上一篇博客的介紹。
TCP和UDP還有另外一個區(qū)別。(2)UDP不能保證有序傳輸。即UDP不能確保數(shù)據(jù)的發(fā)送和接收順序。
下面就來看看UDP協(xié)議的工作原理,對UDP的工作原理有一個好的理解,對后面介紹的UDP編程也是一個好的基礎(chǔ)。
1.1 UDP的工作原理
UDP將網(wǎng)絡(luò)數(shù)據(jù)流量壓縮成數(shù)據(jù)報的形式,每一個數(shù)據(jù)報用8個字節(jié)(8 X 8位=64位)描述報頭信息,剩余字節(jié)包含具體的傳輸數(shù)據(jù)。UDP報頭(只有8個字節(jié))相當(dāng)于TCP的報頭(至少20個字節(jié))很短,UDP報頭由4個域組成,每個域各占2個字節(jié),具體為源端口、目的端口、用戶數(shù)據(jù)報長度和校驗和,
具體結(jié)構(gòu)見下圖(下面也貼出了TCP報文的結(jié)構(gòu)圖,與UDP數(shù)據(jù)報做一個對比的作用):
UDP協(xié)議和TCP協(xié)議都使用端口號為不同的應(yīng)用保留其各自的數(shù)據(jù)傳輸通道這一機(jī)制,數(shù)據(jù)發(fā)送方將UDP數(shù)據(jù)報通過源端口發(fā)送出去,而數(shù)據(jù)接收方則通過目標(biāo)端口接收數(shù)據(jù)。
1.2 UDP的優(yōu)勢
前面介紹中說UDP相對于TCP是不可靠的,不能保證有序傳輸的傳輸協(xié)議,然而UDP協(xié)議相對于TCP協(xié)議的優(yōu)勢在哪里呢?,
UDP相對于TCP的優(yōu)勢主要有三個方面的:
(1)UDP速度比TCP快。
由于UDP不需要先與對方建立連接,也不需要傳輸確認(rèn),因此其數(shù)據(jù)的傳輸速度比TCP快很多。對于一些著重傳輸性能而不是傳輸完整性的應(yīng)用(網(wǎng)絡(luò)音頻播放、視頻點(diǎn)播和網(wǎng)絡(luò)會議等),使用UDP協(xié)議更加適合,因為它傳輸速度快,使通過網(wǎng)絡(luò)播放的視頻音質(zhì)好、畫面清晰。
(2)UDP有消息邊界。
通過UDP協(xié)議進(jìn)行傳輸?shù)陌l(fā)送方對應(yīng)用程序交下來的報文,在添加首部后就向下直接交付給IP層。既不拆分也不合并,而是保留這些報文的邊界,所以使用UDP協(xié)議不需要像TCP那樣考慮消息邊界的問題,這樣就使得UDP編程相對于TCP在接收到的數(shù)據(jù)處理方面要簡單的多。(對于TCP消息邊界的問題可以查看相關(guān)的文檔,在這里我就不列出來了)
(3)UDP可以一對多傳輸
由于傳輸數(shù)據(jù)部建立連接,也就不需要維護(hù)連接狀態(tài),因此一臺服務(wù)器可以同時向多個客戶端發(fā)送相同的信息。利用UDP可以使用廣播或者組播的方式同時向子網(wǎng)的所有客戶端進(jìn)程發(fā)送信息,廣播和組播的介紹放到后面TCP編程中介紹。
上面介紹了UDP協(xié)議相對于TCP協(xié)議的優(yōu)勢,其中速度快是UDP的最重要的優(yōu)勢,也是像一些網(wǎng)絡(luò)會議、即時通信軟件傳輸層選擇UDP協(xié)議進(jìn)行傳輸?shù)脑蛩凇?/p>
二、.net平臺對UDP編程的支持
介紹完UDP相對于TCP的優(yōu)勢后,當(dāng)然很希望在.net平臺下開發(fā)一個基于UDP協(xié)議的一個應(yīng)用了,然后.net平臺下對UDP編程也做了很好的支持,為我們開發(fā)基于UDP協(xié)議的網(wǎng)絡(luò)應(yīng)用提供很多方便之處,下面就簡單介紹.net平臺下對UDP編程的支持(主要介紹提供的類來對UDP協(xié)議進(jìn)行編程)。
.net類庫中的UdpClient類對基礎(chǔ)的Socket進(jìn)行了封裝,這樣就在發(fā)送和接受數(shù)據(jù)時不需要考慮底層套接字的收發(fā)時處理的一些細(xì)節(jié)問題,這樣為UDP編程提供了方便,也可以提高開發(fā)效率(感覺net就是做這樣的事情的,對一些底層的實(shí)現(xiàn)進(jìn)行封裝,方便我們的調(diào)用,這也體現(xiàn)了面向?qū)ο笳Z言的封裝特性)對于這個的具體的使用我就不做過多的介紹的,在后面的UDP編程的實(shí)現(xiàn)部分將會對該類中主要方法的使用,大家可以查看MSDN來查看該類中其他成員的使用: http://msdn.microsoft.com/zh-cn/library/System.Net.Sockets.UdpClient.aspx
三、UDP編程的具體實(shí)現(xiàn)
由于UDP進(jìn)程在通信之前是不需要建立連接,消息接收方可能并不知道是誰給它發(fā)的消息,因此UDP編程分為兩種模式:一種“實(shí)名發(fā)送”,即接收方可以由收到的消息得知發(fā)送方進(jìn)程端口,另外一種則為“匿名發(fā)送”,即接收方并不知道發(fā)給它信息的遠(yuǎn)程進(jìn)程究竟來自哪個端口。下面通過一個winform 程序來演示下UDP的編程:
實(shí)現(xiàn)代碼:
using System;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;using System.Windows.Forms; namespace UDPClient { public partial class frmUdp : Form { private UdpClient sendUdpClient; private UdpClient receiveUpdClient; public frmUdp() { InitializeComponent(); IPAddress[] ips= DNS.GetHostAddresses(""); tbxlocalip.Text= ips[3].ToString(); int port = 51883; tbxlocalPort.Text= port.ToString(); tbxSendtoIp.Text= ips[3].ToString(); tbxSendtoport.Text= port.ToString(); } // 接受消息 private void btnReceive_Click(object sender, EventArgs e) { // 創(chuàng)建接收套接字 IPAddress localIp = IPAddress.Parse(tbxlocalip.Text); IPEndPoint localIpEndPoint= new IPEndPoint(localIp, int.Parse(tbxlocalPort.Text)); receiveUpdClient= new UdpClient(localIpEndPoint); Thread receiveThread= new Thread(ReceiveMessage); receiveThread.Start(); } // 接收消息方法 private void ReceiveMessage() { IPEndPoint remoteIpEndPoint= new IPEndPoint(IPAddress.Any, 0); while (true) { try { // 關(guān)閉receiveUdpClient時此時會產(chǎn)生異常 byte[] receiveBytes = receiveUpdClient.Receive(ref remoteIpEndPoint); string message = Encoding.Unicode.GetString(receiveBytes); // 顯示消息內(nèi)容 ShowMessageforView(lstbxMessageView, string.Format("{0}[{1}]", remoteIpEndPoint, message)); } catch { break; } } } // 利用委托回調(diào)機(jī)制實(shí)現(xiàn)界面上消息內(nèi)容顯示 delegate void ShowMessageforViewCallBack(ListBox listbox, string text); private void ShowMessageforView(ListBox listbox, string text) { if (listbox.InvokeRequired) { ShowMessageforViewCallBack showMessageforViewCallback= ShowMessageforView; listbox.Invoke(showMessageforViewCallback,new object[] { listbox, text }); } else { lstbxMessageView.Items.Add(text); lstbxMessageView.SelectedIndex= lstbxMessageView.Items.Count - 1; lstbxMessageView.ClearSelected(); } } private void btnSend_Click(object sender, EventArgs e) { if (tbxMessageSend.Text == string.Empty) { MessageBox.Show("發(fā)送內(nèi)容不能為空","提示"); return; } // 選擇發(fā)送模式 if (chkbxAnonymous.Checked == true) { // 匿名模式(套接字綁定的端口由系統(tǒng)隨機(jī)分配) sendUdpClient = new UdpClient(0); } else { // 實(shí)名模式(套接字綁定到本地指定的端口) IPAddress localIp = IPAddress.Parse(tbxlocalip.Text); IPEndPoint localIpEndPoint= new IPEndPoint(localIp, int.Parse(tbxlocalPort.Text)); sendUdpClient= new UdpClient(localIpEndPoint); } Thread sendThread= new Thread(SendMessage); sendThread.Start(tbxMessageSend.Text); } // 發(fā)送消息方法 private void SendMessage(object obj) { string message = (string)obj; byte[] sendbytes = Encoding.Unicode.GetBytes(message); IPAddress remoteIp= IPAddress.Parse(tbxSendtoIp.Text); IPEndPoint remoteIpEndPoint= new IPEndPoint(remoteIp, int.Parse(tbxSendtoport.Text)); sendUdpClient.Send(sendbytes, sendbytes.Length, remoteIpEndPoint); sendUdpClient.Close(); // 清空發(fā)送消息框 ResetMessageText(tbxMessageSend); } // 采用了回調(diào)機(jī)制 // 使用委托實(shí)現(xiàn)跨線程界面的操作方式 delegate void ResetMessageCallback(TextBox textbox); private void ResetMessageText(TextBox textbox) { // Control.InvokeRequired屬性代表 // 如果控件的處理與調(diào)用線程在不同線程上創(chuàng)建的,則為true,否則為false if (textbox.InvokeRequired) { ResetMessageCallback resetMessagecallback= ResetMessageText; textbox.Invoke(resetMessagecallback,new object[] { textbox }); } else { textbox.Clear(); textbox.Focus(); } } // 停止接收 private void btnStop_Click(object sender, EventArgs e) { receiveUpdClient.Close(); } // 清空接受消息框 private void btnClear_Click(object sender, EventArgs e) { this.lstbxMessageView.Items.Clear(); } } }
運(yùn)行結(jié)果:
實(shí)名發(fā)送:
在本地運(yùn)行本程序的三個進(jìn)程(分別為A,B,C),把進(jìn)程C做為接受進(jìn)程,進(jìn)程A和進(jìn)程B都向進(jìn)程C發(fā)信息,進(jìn)程A和進(jìn)程分別綁定端口號為11883和21883,發(fā)送到端口都為51883,配置界面如下:
首先不勾選“匿名”復(fù)選框,在進(jìn)程C中點(diǎn)擊“接收”按鈕開啟接受線程,在A進(jìn)程和B進(jìn)程中發(fā)送消息框里分別輸入你好,我是1和你好,我是2,然后點(diǎn)擊發(fā)送按鈕,此時在進(jìn)程中就可以看到進(jìn)程A和進(jìn)程B發(fā)來的消息,如下圖:
從圖中可以看出每條消息之前都顯示了消息的準(zhǔn)確來源(包括消息進(jìn)程鎖在的Ip地址和端口號)
匿名發(fā)送:
下面把“匿名”復(fù)選框勾上后,再按照前面的步驟將得到下面的結(jié)果:
從圖中結(jié)果可以看出此時列表中顯示的消息來源的進(jìn)程端口號分別為49439和49440,而不是發(fā)送消息進(jìn)程的真實(shí)端口(11883和21883)
這種UDP只能辨別消息源主機(jī)的Ip地址,而無法知道發(fā)消息的進(jìn)程究竟是哪個端口稱為“匿名發(fā)送”。正如我們平時發(fā)手機(jī)短信一樣,如果我們把認(rèn)識的名字和電話號碼預(yù)先存在通訊錄里,當(dāng)一發(fā)來信息,接受方馬上就可以從來電顯示中看到是誰發(fā)來的(實(shí)名模式);但是如果是陌生人發(fā)來信息或者廣告等信息時,僅看來電顯示,根本不知道對方是誰(匿名模式),QQ發(fā)消息也是一樣的道理。
四、UDP廣播和組播
前面UDP的實(shí)現(xiàn)中發(fā)送數(shù)據(jù)使用的都是一對一(單播)的通信方式,即只將數(shù)據(jù)發(fā)送到某一個進(jìn)程。前面提到UDP可以實(shí)現(xiàn)一對多的傳輸方式,即通過廣播和組播把數(shù)據(jù)發(fā)送給一組進(jìn)程。下面就介紹下UDP廣播和組播的相關(guān)知識。
4.1 廣播和組播的基本概念
雖然利用TCP協(xié)議可以保證數(shù)據(jù)的可靠、有序的傳輸,但是TCP僅支持一對以的傳輸,而且傳輸時需要在發(fā)送端和每一個接受端之間建立單獨(dú)的數(shù)據(jù)通信通道,如果需要實(shí)現(xiàn)網(wǎng)絡(luò)會議、網(wǎng)絡(luò)視頻的點(diǎn)播等功能時要向大量主機(jī)發(fā)送相同的數(shù)據(jù)包,如果采用單播方式逐個節(jié)點(diǎn)傳輸?shù)脑?,將會給發(fā)送方帶來網(wǎng)絡(luò)堵塞等問題,此時可以考慮實(shí)現(xiàn)UDP的多播方式——即廣播和組播來實(shí)現(xiàn)這樣的功能(一對多通信分為廣播和組播兩種形式)。
廣播是指同時向子網(wǎng)中的多臺計算機(jī)發(fā)送消息,并且所有子網(wǎng)中的計算機(jī)都可以接收到發(fā)送方發(fā)來的消息,每個廣播消息包含一個特殊的IP地址,這個IP的中子網(wǎng)內(nèi)主機(jī)標(biāo)志部分的二進(jìn)制都為1,例如,子網(wǎng)掩碼為255.255.255.0,對于子網(wǎng)192.168.0,則這個IP地址為192.168.0.255.
然后廣播消息又分為本地廣播和全球廣播兩種類型, 本地廣播是指向子網(wǎng)中的所有計算機(jī)發(fā)送廣播消息,其他網(wǎng)絡(luò)不會受到本地廣播的影響。
IP地址分為兩部分——網(wǎng)絡(luò)標(biāo)志部分和主機(jī)標(biāo)志部分,這兩部分是靠子網(wǎng)掩碼來區(qū)分的,主機(jī)標(biāo)記部分二進(jìn)制全部為1的地址成為本地廣播地址。例如:
A類網(wǎng)絡(luò)192.168.0.0,使用子網(wǎng)掩碼255.255.0.0,則本地廣播地址為:
對于IPv4來說,全球廣播使用所有位全為1的IP地址,即255.255.255.255,這個廣播地址代表數(shù)據(jù)報的目的地是網(wǎng)絡(luò)上所有設(shè)備,但是由于路由器會自動過濾全球廣播,所以使用這個地址根本就沒有任何意義。
然后當(dāng)接收者分布于多個不同的子網(wǎng)時,廣播將不再適用,此時可以通過組播的方式來實(shí)現(xiàn),組播也叫多路廣播,組播是將信息從一臺計算機(jī)發(fā)送到本網(wǎng)或全網(wǎng)內(nèi)指定的計算機(jī)上,即發(fā)送到那些加入了指定組播組的計算機(jī)上,每臺計算機(jī)都可以通過程序隨時加入某個組播組中,也可以隨時退出來, 就像我們開網(wǎng)了會議一樣,可以隨時加入會議室進(jìn)行開會,會議結(jié)束和會議進(jìn)行中都可以隨意的退出來。
4.2 加入和退出組播組
組播組又稱為多路廣播組,組播地址的范圍在224.0.0.0到239.255.255.255的D類IP地址(至于這個概念大家可以百度百科里面就查看)。任何發(fā)送到組播地址的消息都會被發(fā)送到組內(nèi)所有成員設(shè)備上,組可以使永久的也可以是臨時,大多數(shù)我們使用的都是臨時的,僅在有成員的時候才存在。
使用組播時,注意生命周期(TTL,Time to live)的設(shè),TTL值表示允許路由器轉(zhuǎn)發(fā)的最大次數(shù),當(dāng)達(dá)到這個最大值時,數(shù)據(jù)包就會被丟棄,TTL的默認(rèn)值為1,設(shè)置為1時表明只能在子網(wǎng)中發(fā)送數(shù)據(jù)
加入組播組:
UdpClient類提供了JoinMulticastGroup方法,用于將UdpClient加入到使用指定的IPAddress的組播組中,調(diào)用該方法后,基礎(chǔ)的Socket會自動向路由器發(fā)送數(shù)據(jù)包,用于請求成為組播組的成員,如果成為組播組成員,就可以接收該組播組的數(shù)據(jù)報。至于具體方法的時候會在后面實(shí)現(xiàn)UDP廣播程序中會用到,另外大家也可以查看MSDN,所以這里我就不再列出來了,只是指出這個方法的作用,讓大家知道有這么個方法來調(diào)用。
退出組播組:
同樣利用UdpClient的DropMulticastGroup方法,可以退出組播組,調(diào)用該方法后,基礎(chǔ)Socket會自動向路由器發(fā)送數(shù)據(jù)包,用于請求從指定的組播組里退出,從組中回收UdpClient對象之后,將不再接受發(fā)送到該組播組的數(shù)據(jù)報。
五、總結(jié)
由于時間的關(guān)系,這篇文章就介紹到這里的,至于實(shí)現(xiàn)UDP廣播的程序放在后面一個專題里面的,前面也對廣播和組播的概念進(jìn)行了簡單的介紹,相信大家也對廣播和組播有了個簡單的認(rèn)識(廣播組和組播組說白了就是一個IP地址的集合,其實(shí)實(shí)現(xiàn)UDP廣播的程序和前面實(shí)現(xiàn)單播的程序差不多,只是前面綁定了一個IP地址當(dāng)然也只能發(fā)送到一個IP地址了,也就是所謂的單播,多播和廣播就是發(fā)送的IP地址是一個組,當(dāng)然也就實(shí)現(xiàn)了一對多的傳輸了)。UDP廣播程序的實(shí)現(xiàn)就放在下一個專題和大家分享的,因為我現(xiàn)在要去吃飯了,吃完飯再繼續(xù)和大家介紹,希望大家如果覺得有幫助的話,也可以推薦下,這給我繼續(xù)寫下去的動力,謝謝大家的支持
當(dāng)前標(biāo)題:[C#網(wǎng)絡(luò)編程系列]專題六:UDP編程
URL標(biāo)題:http://muchs.cn/article12/jpgcgc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、網(wǎng)站內(nèi)鏈、云服務(wù)器、手機(jī)網(wǎng)站建設(shè)、小程序開發(fā)、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)