一、UDP介紹
UDP和TCP都是構建在IP層之上傳輸層的協議,但UDP是一種簡單、面向數據報(Sock_Dgram)的無連接協議,提供的是不一定可靠的傳輸服務。
然而TCP是一種面向連接、可靠的,面向字節流(Sock_Stream)的傳輸協議,對於“無連接”是指在正式通信前不必與對方先建立連接,不管對方狀態如何都可以直接發送過去(就如QQ中通過QQ號查看好友后發送添加好友請求,此間不需要考慮對方的狀態如何,都照樣發送請求)。從UDP和TCP的定義中就可以看出它們兩者的區別了,(1)UDP的可靠性不如TCP,因為TCP傳輸前要首先建立連接,這樣就增加了TCP傳輸的可靠性,所以UDP也被稱為不可靠的傳輸協議,關於TCP的介紹可以看我上一篇博客的介紹。
TCP和UDP還有另外一個區別。(2)UDP不能保證有序傳輸。即UDP不能確保數據的發送和接收順序。
下面就來看看UDP協議的工作原理,對UDP的工作原理有一個好的理解,對后面介紹的UDP編程也是一個好的基礎。
1.1 UDP的工作原理
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協議進行傳輸的原因所在。
二、.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
三、UDP編程的具體實現
由於UDP進程在通信之前是不需要建立連接,消息接收方可能並不知道是誰給它發的消息,因此UDP編程分為兩種模式:一種“實名發送”,即接收方可以由收到的消息得知發送方進程端口,另外一種則為“匿名發送”,即接收方並不知道發給它信息的遠程進程究竟來自哪個端口。下面通過一個winform 程序來演示下UDP的編程:
實現代碼:
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) { // 創建接收套接字 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 { // 關閉receiveUdpClient時此時會產生異常 byte[] receiveBytes = receiveUpdClient.Receive(ref remoteIpEndPoint); string message = Encoding.Unicode.GetString(receiveBytes); // 顯示消息內容 ShowMessageforView(lstbxMessageView, string.Format("{0}[{1}]", remoteIpEndPoint, message)); } catch { break; } } } // 利用委托回調機制實現界面上消息內容顯示 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("發送內容不能為空","提示"); return; } // 選擇發送模式 if (chkbxAnonymous.Checked == true) { // 匿名模式(套接字綁定的端口由系統隨機分配) sendUdpClient = new UdpClient(0); } else { // 實名模式(套接字綁定到本地指定的端口) 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); } // 發送消息方法 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,