zoukankan      html  css  js  c++  java
  • UDP信息接收与发送

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

    一、摘要

       总结基于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} :
     {1}
    ", 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();
    学无先后,达者为师
  • 相关阅读:
    js回调函数
    axios如何先请求A接口然后在请求B接口
    蓝桥杯省赛 区间移位(二分+玄学贪心)
    P1403 [AHOI2005]约数研究(筛法)
    P1029 最大公约数和最小公倍数问题(数论水题)
    洛谷P1147连续自然数和(前缀和)
    洛谷P1017进制转换(进制转换/取模)
    洛谷P1088火星人(stl/全排列)
    Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) D. Navigation System(最短路)
    Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) C. Remove Adjacent(贪心+暴力)
  • 原文地址:https://www.cnblogs.com/seanchang/p/5202743.html
Copyright © 2011-2022 走看看