首页 > 代码库 > 十、Socket之UDP编程

十、Socket之UDP编程

UDP基础知识

  UDP(User Datagram Protocol,用户数据报协议)是一个简单的、面向数据报的无连接协议,提供了快速但不一定可靠的传输服务。

  UDP与TCP相比主要有以下区别。

    1.UDP速度比TCP快

      由于UDP不需要先与对方建立连接,也不需要传输确认,因此其数据传输速度比TCP快得多。

    2.UDP有消息边界

      使用UDP不需要考虑消息边界问题,使用上比TCP简单

    3.UDP可以一对多传输

      利用UDP可以使用广播或组播的方式同时向子网上的所有客户发送信息。这一点也比TCP方便。

    4.UDP可靠性不如TCP

      与TCP不同,UDP并不提供数据传送的保证机制。如果在从发送方到接收方的传递过程中出现数据报的丢失,协议本身并不能做出任何检测或提示。因此,通常人们把UDP称为不可靠的传输协议。

    5.UDP不像TCP那样能保证有序传输

      UDP不能确保数据的发送和接收顺序。对于突发性的数据报,有可能会乱序。事实上,UDP的这种乱序性基本上很少出现,通常只会在网络非常拥挤的情况下才有可能发生

UDP应用编程技术

  UdpClient类

  UdpClient类提供了发送和接收无连接的UDP数据报的方便的方法。封装了底层的套接字,并分别提供了对套接字进一步封装后的同步和异步操作的方法,降低了UDP应用编程的难度。 与TCP协议有TcpListener类和TcpClient类不同,UDP协议只有UdpClient类,这是因为UDP协议是无连接的协议,所以只需要一种Socket。

  创建UdpClient的四种方式:

//1、自动分配合适的本地IPv4地址,并将基础socket与指定的本地端口号绑定。例如:
            UdpClient udpClient = new UdpClient(0);//表示让系统自动为其分配一个合适的端口号

            //2、与包含本地IP地址和端口号的IPEndPoint实例绑定。例如:
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, 51666);
            UdpClient udpClient = new UdpClient(iep);

            //3、自动分配合适的本地IP地址和端口号,用于收发数据,并使用hostname和port参数建立默认远程主机。例如:
            UdpClient udpClient = new UdpClient("192.168.1.100", 11000);

            //4、自动分配合适的本地IPv4地址和端口号,但是该构造函数不执行套接字绑定。如果使用这种构造函数,在发送数据报之前,必须先调用Connect方法,且只能将数据报发送到Connect方法建立的远程主机。例如:
            UdpClient udpClient = new UdpClient();
            //指定默认远程主机和端口号
            udpClient.Connect("192.168.1.100", 51666);
            Byte[] sendBytes = Encoding.Unicode.GetBytes("你好!");
            //发送给默认远程主机
            udpClient.Send(sendBytes, sendBytes.Length);

  同步发送和接收数据

    编写基于UDP的应用程序时,关键在于如何实现数据的发送和接收。由于UDP不需要建立连接,因此可以在任何时候直接向网络中的任意主机发送UDP数据。在同步阻塞方式下,可以使用UdpClient对象的Send方法和Receive方法。

    Send:可直接将数据发送到远程主机,并返回发送数据的长度,Send方法发送数据的类型为byte数组。

    Receive:能够在指定的本地IP地址和端口上接收数据,该方法带一个引用类型的IPEndPoint实例,并将接收到的数据作为byte数组返回。

    发送数据:

      UdpClient myUdpClient = new UdpClient();
            try
            {
                IPEndPoint iep = new IPEndPoint(ip, port);
                byte[] bytes = System.Text.Encoding.Unicode.GetBytes("你好!");
                //UdpClient.Send (Byte[], Int32, IPEndPoint) 
                //Byte[]:待发送到远程主机的数据
                //Int32: 待发送到远程主机的数据长度
                //IPEndPoint :远程主机端点,包含了应用程序连接到主 机上的服务所需的IP地址和端口信息。
                myUdpClient.Send(bytes, bytes.Length, iep);
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message, "发送失败");
            }

    接收数据:

UdpClient udpClient = new UdpClient(IPAddress.Parse("192.168.1.100"), 18001);
            //IPAddress.Any表示对方的IP地址可以是任何IP地址
            //0表示对方的端口号可以是任何端口号。
            IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
            try
            {
                Byte[] receiveBytes = udpClient.Receive(ref remoteIpEndPoint);//表示发送方的IP地址和端口号
                string receiveData =http://www.mamicode.com/ Encoding.Unicode.GetString(receiveBytes);
                Console.WriteLine("接收到信息:" + receiveData);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }

  异步发送和接收数据

    对于UdpClient对象的每个同步方法,都有与之对应的异步BeginSend和EndSend方法。

  发送数据:

 UdpClient sendUdpClient = new UdpClient();
            IPAddress remoteIP;
            if (IPAddress.TryParse(textBoxRemoteIP.Text, out remoteIP) == false)
            {
                MessageBox.Show("远程IP格式不正确");
                return;
            }
            iep = new IPEndPoint(remoteIP, port);
            sendMessage = textBoxSend.Text;
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMessage);
            try
            {
                //异步发送数据
                IAsyncResult ar=
                sendUdpClient.BeginSend(bytes, bytes.Length, iep, null, sendUdpClient);
                
                textBoxSend.Clear();
                textBoxSend.Focus();
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message, "发送失败");
            }
  

  接受数据:

  //在本机指定的端口接收
            UdpState udpState = new UdpState();
            udpState.IpEndPoint = null;
            udpState.MyudpClient = receiveClient;
            //接收从远程主机发送过来的信息;
            IAsyncResult ar =  udpState.MyudpClient.BeginReceive(null, udpState);
            ar.AsyncWaitHandle.WaitOne();

利用UDP协议进行广播和组播

  TCP通信采用一对一的通信模式。日常生活中的网络会议通知、广告、网络信息公告等功能,需要采用UDP实现一对多的群发功能。

   通过Internet实现群发功能的形式有两种:

   (1)利用广播向子网中的所有客户发送消息,比如各类通知、单位公告、集体活动日程安排等;

   (2)利用组播向Internet网上不同的子网发送消息,比如集团向其所属的公司或用户子网发布信息公告等。

  组播和广播的区别

  (1)本地广播无法通过路由器,广播只是限于本网段内,遇到路由器则止。同网段内其他在指定端口侦听的程序都能收到该包都需要一个数据copy。组播没有这个限制,只要加入组就能收到数据包。

  (2)由于广播是向某个子网中的所有计算机用户发送消息,没有目的性,会增加网络传输负担,而且资源消耗较高。组播将消息发送到加入指定组播组的计算机中,组播组是开放的,每台计算机都可以通过程序随时加入到组播组中,也可以随时离开,因此减少了不必要的网络传输负担。

  加入和退出组播组

  组播组:是分享一个组播地址的一组设备。任何发送到组播地址的消息都会被发送到组内的所有成员设备上。 组可以是永久的,也可以是临时的。大多数组播组是临时的,仅在有成员的时候才存在。

  组播地址:组播地址是范围在224.0.0.0到239.255.255.255的D类IP地址。 IP组播使用特殊的IP地址范围来表示不同的组播组。 用户创建一个新的组播组时只需从地址范围内选出一个地址,然后为这个地址构造一个对象,就可以开始发送消息了。

  加入组播组

 //创建UdpClient的实例并设置使用的本地端口号
            UdpClient udpClient = new UdpClient(8001);
            udpClient.JoinMulticastGroup(IPAddress.Parse("224.100.0.1"));
            //或则
            UdpClient udpClient = new UdpClient(8001);
            udpClient.JoinMulticastGroup(IPAddress.Parse("224.100.0.1"), 50);//其中50为TTL值。
 //生存周期:TTL 
            //使用组播时,应注意的是TTL(Time To Live,生存周期)值
            //的设置。TTL值是允许路由器转发的最大数目,当达到这个
            //最大值时,数据包就会被丢弃。如果使用默认值(默认值为
            //1),则只能在子网中发送。可以通过UdpClient对象的Ttl属
            //性直接设置TTL值,例如:
            UdpClient myUdpClient = new UdpClient();
            myUdpClient. TTL = 50;
            //该语句设置TTL值为50,即最多允许50次路由器转发。

  退出组播组

//利用UdpClient的DropMulticastGroup方法,可以退出组播组。参数中指出要退出的多路广播组的IPAddress实例。
//UdpClient从组中收回之后,将不能再接收发送到该组的数据报。
//例如:
    udpClient.DropMulticastGroup(IPAddress.Parse("224.100.0.1"));

  利用组播实现群发功能

    1、广播的通信模式虽然能够实现一对多的通信需要,但是,由于广播是向子网中的所有计算机用户发送消息,没有目的性,不但增加了网络传输负担,而且资源消耗较高。

    2、组播也叫多路广播。所谓组播是将消息从一台计算机发送到本网或全网内选择的计算机子集上,即发送到那些加入指定组播组的计算机上。

  网络会议demo