專題六:UDP編程

引用:html

  前一個專題簡單介紹了TCP編程的一些知識,UDP與TCP地位至關的另外一個傳輸層協議,它也是當下流行的不少主流網絡應用(例如QQ、MSN和Skype等一些即時通訊軟件傳輸層都是應用UDP協議的)底層的傳輸基礎,因此在本專題中就簡單介紹下UDP的工做原理和UDP編程的只是,但願能夠對剛接觸網絡編程的朋友起到入門的做用。編程

1、UDP介紹服務器

  UDP和TCP都是構建在IP層之上傳輸層的協議,但UDP是一種簡單、面向數據報(Sock_Dgram)無鏈接協議,提供的是不必定可靠的傳輸服務。網絡

然而TCP是一種面向鏈接、可靠的,面向字節流(Sock_Stream)的傳輸協議,對於「無鏈接」是指在正式通訊前沒必要與對方先創建鏈接,無論對方狀態如何均可以直接發送過去(就如QQ中經過QQ號查看好友後發送添加好友請求,此間不須要考慮對方的狀態如何,都照樣發送請求)。從UDP和TCP的定義中就能夠看出它們二者的區別:性能

  (1)UDP的可靠性不如TCP,由於TCP傳輸前要首先創建鏈接,這樣就增長了TCP傳輸的可靠性,因此UDP也被稱爲不可靠的傳輸協議,關於TCP的介紹能夠看我上一篇博客的介紹。ui

  (2)UDP不能保證有序傳輸。即UDP不能確保數據的發送和接收順序。this

下面就來看看UDP協議的工做原理,對UDP的工做原理有一個好的理解,對後面介紹的UDP編程也是一個好的基礎。spa

1.1 UDP的工做原理.net

  UDP將網絡數據流量壓縮成數據報的形式,每個數據報用8個字節(8 X 8位=64位)描述報頭信息,剩餘字節包含具體的傳輸數據。UDP報頭(只有8個字節)至關於TCP的報頭(至少20個字節)很短,UDP報頭由4個域組成,每一個域各佔2個字節,具體爲源端口、目的端口、用戶數據報長度和校驗和。線程

  具體結構見下圖(下面也貼出了TCP報文的結構圖,與UDP數據報作一個對比的做用):

UDP協議和TCP協議都使用端口號爲不一樣的應用保留其各自的數據傳輸通道這一機制,數據發送方將UDP數據報經過源端口發送出去,而數據接收方則經過目標端口接收數據。

  

1.2 UDP的優點

  前面介紹中說UDP相對於TCP是不可靠的,不能保證有序傳輸的傳輸協議,然而UDP協議相對於TCP協議的優點在哪裏呢?,

  UDP相對於TCP的優點主要有三個方面的:

      (1)UDP速度比TCP快。

          因爲UDP不須要先與對方創建鏈接,也不須要傳輸確認,所以其數據的傳輸速度比TCP快不少。對於一些着重傳輸性能而不是傳輸完整性的應用(網絡音頻播放、視頻點播和網絡會議等),使用UDP協議更加適合,由於它傳輸速度快,使經過網絡播放的視頻音質好、畫面清晰。

     (2)UDP有消息邊界。

     經過UDP協議進行傳輸的發送方對應用程序交下來的報文,在添加首部後就向下直接交付給IP層。既不拆分也不合並,而是保留這些報文的邊界,因此使用UDP協議不須要像TCP那樣考慮消息邊界的問題,這樣就使得UDP編程相對於TCP在接收到的數據處理方面要簡單的多。(對於TCP消息邊界的問題能夠查看相關的文檔,在這裏我就不列出來了)

     (3)UDP能夠一對多傳輸

     因爲傳輸數據部創建鏈接,也就不須要維護鏈接狀態,所以一臺服務器能夠同時向多個客戶端發送相同的信息。利用UDP可使用廣播或者組播的方式同時向子網的全部客戶端進程發送信息,廣播和組播的介紹放到後面TCP編程中介紹。

 上面介紹了UDP協議相對於TCP協議的優點,其中速度快是UDP的最重要的優點,也是像一些網絡會議、即時通訊軟件傳輸層選擇UDP協議進行傳輸的緣由所在。

2、.net平臺對UDP編程的支持

  介紹完UDP相對於TCP的優點後,固然很但願在.net平臺下開發一個基於UDP協議的一個應用了,而後.net平臺下對UDP編程也作了很好的支持,爲咱們開發基於UDP協議的網絡應用提供不少方便之處,下面就簡單介紹.net平臺下對UDP編程的支持(主要介紹提供的類來對UDP協議進行編程)。

  .net類庫中的UdpClient類對基礎的Socket進行了封裝,這樣就在發送和接受數據時不須要考慮底層套接字的收發時處理的一些細節問題,這樣爲UDP編程提供了方便,也能夠提升開發效率(感受net就是作這樣的事情的,對一些底層的實現進行封裝,方便咱們的調用,這也體現了面嚮對象語言的封裝特性)對於這個的具體的使用我就不作過多的介紹的,在後面的UDP編程的實現部分將會對該類中主要方法的使用,你們能夠查看MSDN來查看該類中其餘成員的使用: http://msdn.microsoft.com/zh-cn/library/System.Net.Sockets.UdpClient.aspx

3、UDP編程的具體實現

   因爲UDP進程在通訊以前是不須要創建鏈接,消息接收方可能並不知道是誰給它發的消息,所以UDP編程分爲兩種模式:一種「實名發送」,即接收方能夠由收到的消息得知發送方進程端口,另一種則爲「匿名發送」,即接收方並不知道發給它信息的遠程進程究竟來自哪一個端口。下面經過一個winform 程序來演示下UDP的編程:

實現代碼:

  1 using System;
  2 using System.Net;
  3 using System.Net.Sockets;
  4 using System.Text;
  5 using System.Threading;
  6 using System.Windows.Forms;
  7 
  8 namespace UDPClient
  9 {
 10     public partial class frmUdp : Form
 11     {
 12         private UdpClient sendUdpClient;
 13         private UdpClient receiveUpdClient;
 14         public frmUdp()
 15         {
 16             InitializeComponent();
 17             IPAddress[] ips = Dns.GetHostAddresses("");
 18             tbxlocalip.Text = ips[3].ToString();
 19             int port = 51883;
 20             tbxlocalPort.Text = port.ToString();
 21             tbxSendtoIp.Text = ips[3].ToString();
 22             tbxSendtoport.Text = port.ToString();
 23         }
 24 
 25         // 接受消息
 26         private void btnReceive_Click(object sender, EventArgs e)
 27         {
 28             // 建立接收套接字
 29             IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
 30             IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalPort.Text));
 31             receiveUpdClient = new UdpClient(localIpEndPoint);
 32 
 33 
 34             Thread receiveThread = new Thread(ReceiveMessage);
 35             receiveThread.Start();
 36         }
 37 
 38         // 接收消息方法
 39         private void ReceiveMessage()
 40         {
 41             IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
 42             while (true)
 43             {
 44                 try
 45                 {
 46                     // 關閉receiveUdpClient時此時會產生異常
 47                     byte[] receiveBytes = receiveUpdClient.Receive(ref remoteIpEndPoint);
 48 
 49                     string message = Encoding.Unicode.GetString(receiveBytes);
 50 
 51                     // 顯示消息內容
 52                     ShowMessageforView(lstbxMessageView, string.Format("{0}[{1}]", remoteIpEndPoint, message));
 53                 }
 54                 catch
 55                 {
 56                     break;
 57                 }
 58             }
 59         }
 60 
 61         // 利用委託回調機制實現界面上消息內容顯示
 62         delegate void ShowMessageforViewCallBack(ListBox listbox, string text);
 63         private void ShowMessageforView(ListBox listbox, string text)
 64         {
 65             if (listbox.InvokeRequired)
 66             {
 67                 ShowMessageforViewCallBack showMessageforViewCallback = ShowMessageforView;
 68                 listbox.Invoke(showMessageforViewCallback, new object[] { listbox, text });
 69             }
 70             else
 71             {
 72                 lstbxMessageView.Items.Add(text);
 73                 lstbxMessageView.SelectedIndex = lstbxMessageView.Items.Count - 1;
 74                 lstbxMessageView.ClearSelected();
 75             }
 76         }
 77         private void btnSend_Click(object sender, EventArgs e)
 78         {
 79             if (tbxMessageSend.Text == string.Empty)
 80             {
 81                 MessageBox.Show("發送內容不能爲空","提示");
 82                 return;
 83             }
 84 
 85             // 選擇發送模式
 86             if (chkbxAnonymous.Checked == true)
 87             {
 88                 // 匿名模式(套接字綁定的端口由系統隨機分配)
 89                 sendUdpClient = new UdpClient(0);
 90             }
 91             else
 92             {
 93                 // 實名模式(套接字綁定到本地指定的端口)
 94                 IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
 95                 IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalPort.Text));
 96                 sendUdpClient = new UdpClient(localIpEndPoint);
 97             }
 98 
 99             Thread sendThread = new Thread(SendMessage);
100             sendThread.Start(tbxMessageSend.Text);
101         }
102 
103         // 發送消息方法
104         private void SendMessage(object obj)
105         {
106             string message = (string)obj;
107             byte[] sendbytes = Encoding.Unicode.GetBytes(message);
108             IPAddress remoteIp = IPAddress.Parse(tbxSendtoIp.Text);
109             IPEndPoint remoteIpEndPoint = new IPEndPoint(remoteIp, int.Parse(tbxSendtoport.Text));
110             sendUdpClient.Send(sendbytes, sendbytes.Length, remoteIpEndPoint);
111           
112             sendUdpClient.Close();
113            
114             // 清空發送消息框
115             ResetMessageText(tbxMessageSend);
116         }
117 
118         // 採用了回調機制
119         // 使用委託實現跨線程界面的操做方式
120         delegate void ResetMessageCallback(TextBox textbox);
121         private void ResetMessageText(TextBox textbox)
122         {
123             // Control.InvokeRequired屬性表明
124             // 若是控件的處理與調用線程在不一樣線程上建立的,則爲true,不然爲false
125             if (textbox.InvokeRequired)
126             {
127                 ResetMessageCallback resetMessagecallback = ResetMessageText;
128                 textbox.Invoke(resetMessagecallback, new object[] { textbox });
129             }
130             else
131             {
132                 textbox.Clear();
133                 textbox.Focus();
134             }
135         }
136 
137         // 中止接收
138         private void btnStop_Click(object sender, EventArgs e)
139         {
140             receiveUpdClient.Close();
141         }
142 
143         // 清空接受消息框
144         private void btnClear_Click(object sender, EventArgs e)
145         {
146             this.lstbxMessageView.Items.Clear();
147         }
148     }
149 }

運行結果:

實名發送:

在本地運行本程序的三個進程(分別爲A,B,C),把進程C作爲接受進程,進程A和進程B都向進程C發信息,進程A和進程分別綁定端口號爲11883和21883,發送到端口都爲51883,配置界面以下:

首先不勾選「匿名」複選框,在進程C中點擊「接收」按鈕開啓接受線程,在A進程和B進程中發送消息框裏分別輸入你好,我是1和你好,我是2 ,而後點擊發送按鈕,此時在進程中就能夠看到進程A和進程B發來的消息,以下圖:

從圖中能夠看出每條消息以前都顯示了消息的準確來源(包括消息進程鎖在的Ip地址和端口號)

匿名發送:

下面把「匿名」複選框勾上後,再按照前面的步驟將獲得下面的結果:

  從圖中結果能夠看出此時列表中顯示的消息來源的進程端口號分別爲49439和49440,而不是發送消息進程的真實端口(11883和21883)

  這種UDP只能辨別消息源主機的Ip地址,而沒法知道發消息的進程到底是哪一個端口稱爲「匿名發送」。正如咱們平時發手機短信同樣,若是咱們把認識的名字和電話號碼預先存在通信錄裏,當一發來信息,接受方立刻就能夠歷來電顯示中看到是誰發來的(實名模式);可是若是是陌生人發來信息或者廣告等信息時,僅看來電顯示,根本不知道對方是誰(匿名模式),QQ發消息也是同樣的道理。

4、UDP廣播和組播

  前面UDP的實現中發送數據使用的都是一對一(單播)的通訊方式,即只將數據發送到某一個進程。前面提到UDP能夠實現一對多的傳輸方式,即經過廣播和組播把數據發送給一組進程。下面就介紹下UDP廣播和組播的相關知識。

4.1 廣播和組播的基本概念

  雖然利用TCP協議能夠保證數據的可靠、有序的傳輸,可是TCP僅支持一對一的傳輸,並且傳輸時須要在發送端和每個接受端之間創建單獨的數據通訊通道,若是須要實現網絡會議、網絡視頻的點播等功能時要向大量主機發送相同的數據包,若是採用單播方式逐個節點傳輸的話,將會給發送方帶來網絡堵塞等問題,此時能夠考慮實現UDP的多播方式——即廣播和組播來實現這樣的功能(一對多通訊分爲廣播和組播兩種形式)。

  廣播是指同時向子網中的多臺計算機發送消息,而且全部子網中的計算機均可以接收到發送方發來的消息,每一個廣播消息包含一個特殊的IP地址,這個IP的中子網內主機標誌部分的二進制都爲1,例如,子網掩碼爲255.255.255.0,對於子網192.168.0,則這個IP地址爲192.168.0.255.

  而後廣播消息又分爲本地廣播和全球廣播兩種類型, 本地廣播是指向子網中的全部計算機發送廣播消息,其餘網絡不會受到本地廣播的影響。

IP地址分爲兩部分——網絡標誌部分和主機標誌部分,這兩部分是靠子網掩碼來區分的,主機標記部分二進制所有爲1的地址成爲本地廣播地址。例如:A類網絡192.168.0.0,使用子網掩碼255.255.0.0,則本地廣播地址爲:
                      

  對於IPv4來講,全球廣播使用全部位全爲1的IP地址,即255.255.255.255,這個廣播地址表明數據報的目的地是網絡上全部設備,可是因爲路由器會自動過濾全球廣播,因此使用這個地址根本就沒有任何意義。

  而後當接收者分佈於多個不一樣的子網時,廣播將再也不適用,此時能夠經過組播的方式來實現,組播也叫多路廣播,組播是將信息從一臺計算機發送到本網或全網內指定的計算機上,即發送到那些加入了指定組播組的計算機上,每臺計算機均可以經過程序隨時加入某個組播組中,也能夠隨時退出來, 就像咱們開網絡會議同樣,能夠隨時加入會議室進行開會,會議結束和會議進行中均可以隨意的退出來。

4.2 加入和退出組播組

  組播組又稱爲多路廣播組,組播地址的範圍在224.0.0.0到239.255.255.255的D類IP地址(至於這個概念你們能夠百度百科裏面就查看)。任何發送到組播地址的消息都會被髮送到組內全部成員設備上,組可使永久的也能夠是臨時,大多數咱們使用的都是臨時的,僅在有成員的時候才存在。

  使用組播時,注意生命週期(TTL,Time to live)的設,TTL值表示容許路由器轉發的最大次數,當達到這個最大值時,數據包就會被丟棄,TTL的默認值爲1,設置爲1時代表只能在子網中發送數據。

加入組播組:

  UdpClient類提供了JoinMulticastGroup方法,用於將UdpClient加入到使用指定的IPAddress的組播組中,調用該方法後,基礎的Socket會自動向路由器發送數據包,用於請求成爲組播組的成員,若是成爲組播組成員,就能夠接收該組播組的數據報。至於具體方法的時候會在後面實現UDP廣播程序中會用到,另外你們也能夠查看MSDN,因此這裏我就再也不列出來了,只是指出這個方法的做用,讓你們知道有這麼個方法來調用。

退出組播組:

  一樣利用UdpClient的DropMulticastGroup方法,能夠退出組播組,調用該方法後,基礎Socket會自動向路由器發送數據包,用於請求從指定的組播組裏退出,從組中回收UdpClient對象以後,將再也不接受發送到該組播組的數據報。

5、總結

   因爲時間的關係,這篇文章就介紹到這裏的,至於實現UDP廣播的程序放在後面一個專題裏面的,前面也對廣播和組播的概念進行了簡單的介紹,相信你們也對廣播和組播有了個簡單的認識(廣播組和組播組說白了就是一個IP地址的集合,其實實現UDP廣播的程序和前面實現單播的程序差很少,只是前面綁定了一個IP地址固然也只能發送到一個IP地址了,也就是所謂的單播,多播和廣播就是發送的IP地址是一個組,固然也就實現了一對多的傳輸了)。UDP廣播程序的實現就放在下一個專題和你們分享的,由於我如今要去吃飯了,吃完飯再繼續和你們介紹,但願你們若是以爲有幫助的話,也能夠推薦下,這給我繼續寫下去的動力,謝謝你們的支持

 本專題源碼地址:http://files.cnblogs.com/zhili/UDPCommunication.zip 

轉自:http://www.cnblogs.com/zhili/archive/2012/09/01/UDP_Multicast.html

相關文章
相關標籤/搜索