总结基于C#的UDP协议的同步通讯。html
Visual Studio 2010数组
UDP传输协议同TCP传输协议的区别可查阅相关文档,此处再也不赘述。服务器
因为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对象。socket
UDP应用不是严格意义上的真正的服务器和客户机,而是平等的关系,即没有主与次的关系。为了简便起见,仍然把下面的这个应用叫作UDP服务器。性能
服务器端代码:spa
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端口号信息。code
客户端代码:orm
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(); } } }
上述代码的实现逻辑为:相关设置完成后,服务器端先向客户端发送信息,以后客户端经过键盘发送字符串,服务器端收到后再发送给客户端,如此循环。
服务器端代码:
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();