欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于C#的UDP协议的同步通信实现代码

程序员文章站 2022-05-22 13:42:38
一、摘要 总结基于c#的udp协议的同步通信。  二、实验平台 visual studio 2010  三、实验原理 udp传输协议同tcp传...

一、摘要

总结基于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();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。