基于C#的UDP协议的同步通信实现代码
一、摘要
总结基于C#的UDP协议的同步通信。
二、实验平台
VisualStudio2010
三、实验原理
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,EndPointRemote)
ReceiveFrom()方法同SendTo()方法类似,但是使用EndPoint对象声明的方式不一样。利用ref修饰,传递的不是一个EndPoint对象,而是将参数传递给一个EndPoint对象。
UDP应用不是严格意义上的真正的服务器和客户机,而是平等的关系,即没有主与次的关系。为了简便起见,仍然把下面的这个应用叫做UDP服务器。
服务器端代码:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Net;
usingSystem.Net.Sockets;
namespaceUDP
{
classProgram
{
staticvoidMain(string[]args)
{
intrecv;
byte[]data=newbyte[1024];
//得到本机IP,设置TCP端口号
IPEndPointip=newIPEndPoint(IPAddress.Any,8001);
Socketnewsock=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
//绑定网络地址
newsock.Bind(ip);
Console.WriteLine("ThisisaServer,hostnameis{0}",Dns.GetHostName());
//等待客户机连接
Console.WriteLine("Waitingforaclient");
//得到客户机IP
IPEndPointsender=newIPEndPoint(IPAddress.Any,0);
EndPointRemote=(EndPoint)(sender);
recv=newsock.ReceiveFrom(data,refRemote);
Console.WriteLine("Messagereceivedfrom{0}:",Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data,0,recv));
//客户机连接成功后,发送信息
stringwelcome="你好!";
//字符串与字节数组相互转换
data=Encoding.ASCII.GetBytes(welcome);
//发送信息
newsock.SendTo(data,data.Length,SocketFlags.None,Remote);
while(true)
{
data=newbyte[1024];
//发送接收信息
recv=newsock.ReceiveFrom(data,refRemote);
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端口号信息。
客户端代码:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Net;
usingSystem.Net.Sockets;
namespaceUDPClient
{
classProgram
{
staticvoidMain(string[]args)
{
byte[]data=newbyte[1024];
stringinput,stringData;
//构建TCP服务器
Console.WriteLine("ThisisaClient,hostnameis{0}",Dns.GetHostName());
//设置服务IP,设置TCP端口号
IPEndPointip=newIPEndPoint(IPAddress.Parse("127.0.0.1"),8001);
//定义网络类型,数据连接类型和网络协议UDP
Socketserver=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
stringwelcome="你好!";
data=Encoding.ASCII.GetBytes(welcome);
server.SendTo(data,data.Length,SocketFlags.None,ip);
IPEndPointsender=newIPEndPoint(IPAddress.Any,0);
EndPointRemote=(EndPoint)sender;
data=newbyte[1024];
//对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制
intrecv=server.ReceiveFrom(data,refRemote);
Console.WriteLine("Messagereceivedfrom{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=newbyte[1024];
recv=server.ReceiveFrom(data,refRemote);
stringData=Encoding.ASCII.GetString(data,0,recv);
Console.WriteLine(stringData);
}
Console.WriteLine("StoppingClient.");
server.Close();
}
}
}
上述代码的实现逻辑为:相关设置完成后,服务器端先向客户端发送信息,之后客户端通过键盘发送字符串,服务器端收到后再发送给客户端,如此循环。
4.2采用UDPClient类实现
服务器端代码:
usingSystem;
usingSystem.Net;
usingSystem.Net.Sockets;
usingSystem.Text;
publicclassCustom
{
//设置IP,IPV6
privatestaticreadonlyIPAddressGroupAddress=IPAddress.Parse("IP地址");
//设置端口
privateconstintGroupPort=11000;
privatestaticvoidStartListener()
{
booldone=false;
UdpClientlistener=newUdpClient();
IPEndPointgroupEP=newIPEndPoint(GroupAddress,GroupPort);
try
{
//IPV6,组播
listener.JoinMulticastGroup(GroupAddress);
listener.Connect(groupEP);
while(!done)
{
Console.WriteLine("Waitingforbroadcast");
byte[]bytes=listener.Receive(refgroupEP);
Console.WriteLine("Receivedbroadcastfrom{0}:\n{1}\n",groupEP.ToString(),Encoding.ASCII.GetString(bytes,0,bytes.Length));
}
listener.Close();
}
catch(Exceptione)
{
Console.WriteLine(e.ToString());
}
}
publicstaticintMain(String[]args)
{
StartListener();
return0;
}
}
客户端代码:
usingSystem;
usingSystem.Net;
usingSystem.Net.Sockets;
usingSystem.Text;
publicclassClient
{
privatestaticIPAddressGroupAddress=IPAddress.Parse("IP地址");
privatestaticintGroupPort=11000;
privatestaticvoidSend(Stringmessage)
{
UdpClientsender=newUdpClient();
IPEndPointgroupEP=newIPEndPoint(GroupAddress,GroupPort);
try
{
Console.WriteLine("Sendingdatagram:{0}",message);
byte[]bytes=Encoding.ASCII.GetBytes(message);
sender.Send(bytes,bytes.Length,groupEP);
sender.Close();
}
catch(Exceptione)
{
Console.WriteLine(e.ToString());
}
}
publicstaticintMain(String[]args)
{
Send(args[0]);
return0;
}
}
以上代码需要说明的是:
(1)上述代码是基于IPV6地址的组播模式。IPv4中的广播(broadcast)可以导致网络性能的下降甚至广播风暴(broadcaststorm)。在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)如果是采用窗体的形式建议使用这种格式,否则在接收数据时可能会出现死机的现象。
//创建一个子线程
Threadthread=newThread(
delegate()
{
try
{
//在这里写你的代码
}
catch(Exception)
{
}
}
);
thread.Start();
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。