基于C#的UDP協(xié)議的同步通信實(shí)現(xiàn)代碼
一、摘要
總結(jié)基于C#的UDP協(xié)議的同步通信。
二、實(shí)驗(yàn)平臺(tái)
Visual Studio 2010
三、實(shí)驗(yàn)原理
UDP傳輸協(xié)議同TCP傳輸協(xié)議的區(qū)別可查閱相關(guān)文檔,此處不再贅述。
四、實(shí)例
4.1 采用socket實(shí)現(xiàn)UDP
由于UDP是一種無(wú)連接的協(xié)議。因此,為了使服務(wù)器應(yīng)用能夠發(fā)送和接收UDP數(shù)據(jù)包,則需要做兩件事情:
(1) 創(chuàng)建一個(gè)Socket對(duì)象;
(2) 將創(chuàng)建的套接字對(duì)象與本地IPEndPoint進(jìn)行綁定。
完成上述步驟后,那么創(chuàng)建的套接字就能夠在IPEndPoint上接收流入的UDP數(shù)據(jù)包,或者將流出的UDP數(shù)據(jù)包發(fā)送到網(wǎng)絡(luò)中其他任意設(shè)備。使用UDP進(jìn)行通信時(shí),不需要連接。因?yàn)楫惖氐闹鳈C(jī)之間沒(méi)有建立連接,所以UDP不能使用標(biāo)準(zhǔn)的Send()和Receive()t套接字方法,而是使用兩個(gè)其他的方法:SendTo()和ReceiveFrom()。
SendTo()方法指定要發(fā)送的數(shù)據(jù),和目標(biāo)機(jī)器的IPEndPoint。該方法有多種不同的使用方法,可以根據(jù)具體的應(yīng)用進(jìn)行選擇,但是至少要指定數(shù)據(jù)包和目標(biāo)機(jī)器。如下:
SendTo(byte[] data,EndPoint Remote)
ReceiveFrom()方法同SendTo()方法類似,但是使用EndPoint對(duì)象聲明的方式不一樣。利用ref修飾,傳遞的不是一個(gè)EndPoint對(duì)象,而是將參數(shù)傳遞給一個(gè)EndPoint對(duì)象。
UDP應(yīng)用不是嚴(yán)格意義上的真正的服務(wù)器和客戶機(jī),而是平等的關(guān)系,即沒(méi)有主與次的關(guān)系。為了簡(jiǎn)便起見(jiàn),仍然把下面的這個(gè)應(yīng)用叫做UDP服務(wù)器。
服務(wù)器端代碼:
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]; //得到本機(jī)IP,設(shè)置TCP端口號(hào) IPEndPoint ip = new IPEndPoint(IPAddress.Any, 8001); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //綁定網(wǎng)絡(luò)地址 newsock.Bind(ip); Console.WriteLine("This is a Server, host name is {0}", Dns.GetHostName()); //等待客戶機(jī)連接 Console.WriteLine("Waiting for a client"); //得到客戶機(jī)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)); //客戶機(jī)連接成功后,發(fā)送信息 string welcome = "你好 ! "; //字符串與字節(jié)數(shù)組相互轉(zhuǎn)換 data = Encoding.ASCII.GetBytes(welcome); //發(fā)送信息 newsock.SendTo(data, data.Length, SocketFlags.None, Remote); while (true) { data = new byte[1024]; //發(fā)送接收信息 recv = newsock.ReceiveFrom(data, ref Remote); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); newsock.SendTo(data, recv, SocketFlags.None, Remote); } } } }
對(duì)于接收流入的UDP服務(wù)器程序來(lái)說(shuō),必須將程序與本地系統(tǒng)中指定的UDP端口進(jìn)行綁定。這就可以通過(guò)使用合適的本地IP地址創(chuàng)建一個(gè)IPEndPoint對(duì)象,以及合適的UDP端口號(hào)。上述范例程序中的UDP服務(wù)器能夠在端口8001從網(wǎng)絡(luò)上接收任意流入的UDP數(shù)據(jù)包。
UDP客戶機(jī)程序與服務(wù)器程序非常類似。
因?yàn)榭蛻魴C(jī)不需要在指定的UDP端口等待流入的數(shù)據(jù),因此,不使用Bind()方法,而是使用在數(shù)據(jù)發(fā)送時(shí)系統(tǒng)隨機(jī)指定的一個(gè)UDP端口,而且使用同一個(gè)端口接收返回的消息。在開(kāi)發(fā)產(chǎn)品時(shí),要為客戶機(jī)指定一套UDP端口,以便服務(wù)器和客戶機(jī)程序使用相同的端口號(hào)。UDP客戶機(jī)程序首先定義一個(gè)IPEndPoint,UDP服務(wù)器將發(fā)送數(shù)據(jù)包到這個(gè)IPEndPoint。如果在遠(yuǎn)程設(shè)備上運(yùn)行UDP服務(wù)器程序,在IPEndPoint定義中必須輸入適當(dāng)?shù)腎P地址和UDP端口號(hào)信息。
客戶端代碼:
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; //構(gòu)建TCP 服務(wù)器 Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName()); //設(shè)置服務(wù)IP,設(shè)置TCP端口號(hào) IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8001); //定義網(wǎng)絡(luò)類型,數(shù)據(jù)連接類型和網(wǎng)絡(luò)協(xié)議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]; //對(duì)于不存在的IP地址,加入此行代碼后,可以在指定時(shí)間內(nèi)解除阻塞模式限制 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(); } } }
上述代碼的實(shí)現(xiàn)邏輯為:相關(guān)設(shè)置完成后,服務(wù)器端先向客戶端發(fā)送信息,之后客戶端通過(guò)鍵盤(pán)發(fā)送字符串,服務(wù)器端收到后再發(fā)送給客戶端,如此循環(huán)。
4.2 采用UDPClient類實(shí)現(xiàn)
服務(wù)器端代碼:
using System; using System.Net; using System.Net.Sockets; using System.Text; public class Custom { // 設(shè)置IP,IPV6 private static readonly IPAddress GroupAddress = IPAddress.Parse("IP地址"); // 設(shè)置端口 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; } }
以上代碼需要說(shuō)明的是:
(1) 上述代碼是基于IPV6地址的組播模式。IPv4中的廣播(broadcast)可以導(dǎo)致網(wǎng)絡(luò)性能的下降甚至廣播風(fēng)暴(broadcast storm)。在IPv6中就不存在廣播這一概念了,取而代之的是組播(multicast)和任意播(anycast)。
(2) IPV6地址表示方法:
a) X:X:X:X:X:X:X:X(每個(gè)X代表16位的16進(jìn)制數(shù)字),不區(qū)分大小寫(xiě);
b) 排頭的0可省略,比如09C0就可以寫(xiě)成9C0,0000可以寫(xiě)成0;
c) 連續(xù)為0的字段可以以::來(lái)代替,但是整個(gè)地址中::只能出現(xiàn)一次,比如FF01:0:0:0:0:0:0:1就可以簡(jiǎn)寫(xiě)成FF01::1。
(3) 如果是采用窗體的形式建議使用這種格式,否則在接收數(shù)據(jù)時(shí)可能會(huì)出現(xiàn)死機(jī)的現(xiàn)象。
// 創(chuàng)建一個(gè)子線程 Thread thread = new Thread( delegate() { try { //在這里寫(xiě)你的代碼 } catch (Exception ) { } } ); thread.Start();
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#實(shí)現(xiàn)自定義單選和復(fù)選按鈕樣式
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)定義單選和復(fù)選按鈕樣式,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12C#使用Dispose模式實(shí)現(xiàn)手動(dòng)對(duì)資源的釋放
這篇文章主要介紹了C#使用Dispose模式實(shí)現(xiàn)手動(dòng)對(duì)資源的釋放,涉及C#采用Dispose模式操作資源的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08C#實(shí)現(xiàn)ProperTyGrid自定義屬性的方法
這篇文章主要介紹了C#實(shí)現(xiàn)ProperTyGrid自定義屬性的方法,主要通過(guò)接口ICustomTypeDescriptor實(shí)現(xiàn),需要的朋友可以參考下2014-09-09C#使用Datatable導(dǎo)入sqlserver數(shù)據(jù)庫(kù)的三種方法
本文主要介紹了C#使用Datatable導(dǎo)入sqlserver數(shù)據(jù)庫(kù)的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08關(guān)于C#結(jié)構(gòu)體 你需要知道的
這篇文章主要介紹了關(guān)于C#結(jié)構(gòu)體的相關(guān)知識(shí),以及使用方法,文中代碼非常詳細(xì),幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06