本篇文章主要介紹了基於C#的UDP協議的同步實現代碼,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
一、摘要
總結基於C#的UDP協議的同步通訊。
二、實驗平台
Visual Studio 2010
三、實驗原理
UDP傳輸協議同TCP傳輸協議的區別可查閱相關文檔,此處不再贅述。
四、執行個體
4.1 採用socket實現UDP
由於UDP是一種不需連線的協議。因此,為了使伺服器應用能夠發送和接收UDP資料包,則需要做兩件事情:
(1) 建立一個Socket對象;
(2) 將建立的通訊端對象與本地IPEndPoint進行綁定。
完成上述步驟後,那麼建立的通訊端就能夠在IPEndPoint上接收流入的UDP資料包,或者將流出的UDP資料包發送到網路中其他任意裝置。使用UDP進行通訊時,不需要串連。因為異地的主機之間沒有建立串連,所以UDP不能使用標準的Send()和Receive()t通訊端方法,而是使用兩個其他的方法:SendTo()和ReceiveFrom()。
SendTo()方法指定要發送的資料,和目標機器的IPEndPoint。該方法有多種不同的使用方法,可以根據具體的應用進行選擇,但是至少要指定資料包和目標機器。如下:
SendTo(byte[] data,EndPoint Remote)
ReceiveFrom()方法同SendTo()方法類似,但是使用EndPoint對象聲明的方式不一樣。利用ref修飾,傳遞的不是一個EndPoint對象,而是將參數傳遞給一個EndPoint對象。
UDP應用不是嚴格意義上的真正的伺服器和客戶機,而是平等的關係,即沒有主與次的關係。為了簡便起見,仍然把下面的這個應用叫做UDP伺服器。
伺服器端代碼:
using System;using System.Collections.Generic;using System.Text;using System.Net;using System.Net.Sockets;namespace UDP{ class Program { static void Main(string[] args) { int recv; byte[] data = new byte[1024]; //得到本機IP,設定TCP連接埠號碼 IPEndPoint ip = new IPEndPoint(IPAddress.Any, 8001); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //綁定網路地址 newsock.Bind(ip); Console.WriteLine("This is a Server, host name is {0}", Dns.GetHostName()); //等待客戶機串連 Console.WriteLine("Waiting for a client"); //得到客戶機IP IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); EndPoint Remote = (EndPoint)(sender); recv = newsock.ReceiveFrom(data, ref Remote); Console.WriteLine("Message received from {0}: ", Remote.ToString()); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); //客戶機串連成功後,發送資訊 string welcome = "你好 ! "; //字串與位元組數組相互轉換 data = Encoding.ASCII.GetBytes(welcome); //發送資訊 newsock.SendTo(data, data.Length, SocketFlags.None, Remote); while (true) { data = new byte[1024]; //發送接收資訊 recv = newsock.ReceiveFrom(data, ref Remote); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); newsock.SendTo(data, recv, SocketFlags.None, Remote); } } }}
對於接收流入的UDP伺服器程式來說,必須將程式與本地系統中指定的UDP連接埠進行綁定。這就可以通過使用合適的本地IP地址建立一個IPEndPoint對象,以及合適的UDP連接埠號碼。上述範常式序中的UDP伺服器能夠在連接埠8001從網路上接收任意流入的UDP資料包。
UDP客戶機程式與伺服器程式非常類似。
因為客戶機不需要在指定的UDP連接埠等待流入的資料,因此,不使用Bind()方法,而是使用在資料發送時系統隨機指定的一個UDP連接埠,而且使用同一個連接埠接收返回的訊息。在開發產品時,要為客戶機指定一套UDP連接埠,以便伺服器和客戶機程式使用相同的連接埠號碼。UDP客戶機程式首先定義一個IPEndPoint,UDP伺服器將發送資料包到這個IPEndPoint。如果在遠程裝置上運行UDP伺服器程式,在IPEndPoint定義中必須輸入適當的IP地址和UDP連接埠號碼資訊。
用戶端代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Net;using System.Net.Sockets;namespace UDPClient{ class Program { static void Main(string[] args) { byte[] data = new byte[1024]; string input, stringData; //構建TCP 伺服器 Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName()); //設定服務IP,設定TCP連接埠號碼 IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8001); //定義網路類型,資料連線類型和網路通訊協定UDP Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); string welcome = "你好! "; data = Encoding.ASCII.GetBytes(welcome); server.SendTo(data, data.Length, SocketFlags.None, ip); IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); EndPoint Remote = (EndPoint)sender; data = new byte[1024]; //對於不存在的IP地址,加入此行代碼後,可以在指定時間內解除阻塞模式限制 int recv = server.ReceiveFrom(data, ref Remote); Console.WriteLine("Message received from {0}: ", Remote.ToString()); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); while (true) { input = Console.ReadLine(); if (input == "exit") break; server.SendTo(Encoding.ASCII.GetBytes(input), Remote); data = new byte[1024]; recv = server.ReceiveFrom(data, ref Remote); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } Console.WriteLine("Stopping Client."); server.Close(); } }}
上述代碼的實現邏輯為:相關設定完成後,伺服器端先向用戶端發送資訊,之後用戶端通過鍵盤發送字串,伺服器端收到後再發送給用戶端,如此迴圈。
4.2 採用UDPClient類實現
伺服器端代碼:
using System;using System.Net;using System.Net.Sockets;using System.Text;public class Custom{ // 設定IP,IPV6 private static readonly IPAddress GroupAddress = IPAddress.Parse("IP地址"); // 設定連接埠 private const int GroupPort = 11000; private static void StartListener() { bool done = false; UdpClient listener = new UdpClient(); IPEndPoint groupEP = new IPEndPoint(GroupAddress, GroupPort); try { //IPV6,組播 listener.JoinMulticastGroup(GroupAddress); listener.Connect(groupEP); while (!done) { Console.WriteLine("Waiting for broadcast"); byte[] bytes = listener.Receive(ref groupEP); Console.WriteLine("Received broadcast from {0} :\n {1}\n", groupEP.ToString(), Encoding.ASCII.GetString(bytes, 0, bytes.Length)); } listener.Close(); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public static int Main(String[] args) { StartListener(); return 0; }}
用戶端代碼:
using System;using System.Net;using System.Net.Sockets;using System.Text;public class Client{ private static IPAddress GroupAddress = IPAddress.Parse("IP地址"); private static int GroupPort = 11000; private static void Send(String message) { UdpClient sender = new UdpClient(); IPEndPoint groupEP = new IPEndPoint(GroupAddress, GroupPort); try { Console.WriteLine("Sending datagram : {0}", message); byte[] bytes = Encoding.ASCII.GetBytes(message); sender.Send(bytes, bytes.Length, groupEP); sender.Close(); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public static int Main(String[] args) { Send(args[0]); return 0; }}
以上代碼需要說明的是:
(1) 上述代碼是基於IPV6地址的組播模式。IPv4中的廣播(broadcast)可以導致網路效能的下降甚至廣播風暴(broadcast storm)。在IPv6中就不存在廣播這一概念了,取而代之的是組播(multicast)和任意播(anycast)。
(2) IPV6地址表示方法:
a) X:X:X:X:X:X:X:X(每個X代表16位的16進位數字),不區分大小寫;
b) 排頭的0可省略,比如09C0就可以寫成9C0,0000可以寫成0;
c) 連續為0的欄位可以以::來代替,但是整個地址中::只能出現一次,比如FF01:0:0:0:0:0:0:1就可以簡寫成FF01::1。
(3) 如果是採用表單的形式建議使用這種格式,否則在接收資料時可能會出現死機的現象。
// 建立一個子線程 Thread thread = new Thread( delegate() { try { //在這裡寫你的代碼 } catch (Exception ) { } } ); thread.Start();