基于C#的UDP协议的同步实现

2019-03-02 23:42|来源: 网路

一、摘要

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

 

 

 

 

 


转自:http://www.cnblogs.com/sunev/archive/2012/08/08/2627247

相关问答

更多
  • 您应该使用ReceiveFrom 。 Receive是针对TCP的。 如果我没有弄错,广播消息不会排队。 在广播和EndReceiveFrom之前调用BeginReceiveFrom 。 You should use ReceiveFrom. Receive is for TCP. If I'm not mistaken, broadcast messages are not queued. Call BeginReceiveFrom before the broadcast and EndReceiveF ...
  • UDP只是简单地发送网络消息而不执行命令,例如,它们可以按顺序发送,而不检查消息是否通过。 TCP强制数据包排序,并且有一个检查消息的方法。 所以它更可靠。 就吞吐量而言 - 例如在给定时间内传输的数据量 - 它们在实践中大致相同。 UDP的优点是延迟较低。 因为它不检查订购或确认收到数据包 - 您的程序在到达时收到数据包。 没有等待确认。 当低延迟非常重要且信息量很小时,您希望使用UDP,并且您的程序可以容忍丢失数据包和无序数据包。 我只见过它用于视频游戏(如射手)发送用户输入。 “teamviewer” ...
  • UDP已经有校验和 。 你是不是想问一下像RUDP这样的东西? 这允许您保证数据包传递和排序(两者,无,或者任何一种)。 Lidgren实现了可靠的UDP协议。 如果你想自己动手,请阅读SEQ和ACK如何在TCP中工作 ; 并通过UDP模仿(这是Lidgren基本上做的)。 UDP already has a checksum. Did you mean to ask about something like RUDP? This allows you to guarantee packet deliver ...
  • UDP数据报是(a)大小有限(b)可以无序传递(c)没有交付保证。 这使得UDP上的对话变得复杂,并且需要从小数据报重建大帧,检测丢弃的包和/或请求重新发送,维护数据报列表或接收和可能发送方。 这使得UDP会话不仅仅是一段代码。 有关基础知识,请查看以下内容: 发送和接收UDP数据包 在C#中发送UDP数据包 UDP datagrams are (a) limited in size (b) can be delivered out of order (c) no delivery guarantee. T ...
  • 使用多播组只能使用IPv6,而IPAddress.Any不适用于IPv6。 你在用什么广播地址? 255.255.255.255往往不能像往常一样工作软件,设备和防火墙不加区分地丢弃这样的广播,但是使用广播到更具体的子网通常是有效的,例如: 192.168.255.255. Using a multicast group will only work with IPv6, which IPAddress.Any is not so that will not work. What broadcast ad ...
  • “强制使用专用套接字。” 对我来说,为每个连接创建一个唯一的套接字,并在整个连接中使用它。 编辑:从服务器的角度来看,只是为了扩展这一点。 UDP套接字不是由远程地址标识的,而是仅由本地地址标识,尽管每条消息都有一个关联的远程地址。 (来源) 。 这样,服务器可以区分每个消息来自哪个客户端。 由于远程地址由IP地址和端口组合组成,因此在整个服务器通信期间应使用相同的套接字。 这是因为如果不这样做,下次更改底层套接字时可能会为您分配一个不同的端口。 “如果服务器与客户端失去UDP连接,它将......” 松散 ...
  • 您可以在同一端口上同时使用TCP和UDP。 也可以看看: TCP和UDP套接字可以使用相同的端口吗? 下面的示例演示了您可以同时发送和接收UDP和TCP消息。 也许,你的UdpClient创建来监听传入的数据报是个问题。 我建议像你的TcpListener一样创建它。 服务器: using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using ...
  • 您正在主UI线程中侦听UDP端口, 这就是为什么当你打电话时你没有看到主窗口 因为UI线程上的控制流被UDP_listening_PI1方法阻止。 您需要在不同的后台线程中侦听UDP端口,然后, 在需要时将消息泵送回主UI线程。 你可能需要下面的东西。 private readonly Dispatcher _uiDispatcher; public MainWindow() { InitializeComponent(); _uiDispatcher ...
  • 很多问题,让我回答我的问题(没有特别的顺序) 数据包越大,碎片的可能性越大,碎片数量越多。 从理论上讲,如果丢失了一个片段,你就丢失了数据包(底层不能重新组装数据包,因此它被丢弃了) 许多消息来源建议UDP数据包大约为576字节(参见http://www.faqs.org/rfcs/rfc791.html ),以确保永远不会发生碎片。 操作系统中有一个相当大的缓冲区(默认情况下,如果我的内存正确,则为4M),但它在内核级别并且不容易查看。 但是,您可以调整Socket类中的ReceiveBufferSize ...
  • 看看这个代码的快速修复,请注意这个结构仅用于演示,不适用于生产。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net; using System.Net.Sockets; using System.Threading; namespace testUDP { class Program { ...