专题六:UDP编程

引用:html

  前一个专题简单介绍了TCP编程的一些知识,UDP与TCP地位至关的另外一个传输层协议,它也是当下流行的不少主流网络应用(例如QQ、MSN和Skype等一些即时通讯软件传输层都是应用UDP协议的)底层的传输基础,因此在本专题中就简单介绍下UDP的工做原理和UDP编程的只是,但愿能够对刚接触网络编程的朋友起到入门的做用。编程

1、UDP介绍服务器

  UDP和TCP都是构建在IP层之上传输层的协议,但UDP是一种简单、面向数据报(Sock_Dgram)无链接协议,提供的是不必定可靠的传输服务。网络

然而TCP是一种面向链接、可靠的,面向字节流(Sock_Stream)的传输协议,对于“无链接”是指在正式通讯前没必要与对方先创建链接,无论对方状态如何均可以直接发送过去(就如QQ中经过QQ号查看好友后发送添加好友请求,此间不须要考虑对方的状态如何,都照样发送请求)。从UDP和TCP的定义中就能够看出它们二者的区别:性能

  (1)UDP的可靠性不如TCP,由于TCP传输前要首先创建链接,这样就增长了TCP传输的可靠性,因此UDP也被称为不可靠的传输协议,关于TCP的介绍能够看我上一篇博客的介绍。ui

  (2)UDP不能保证有序传输。即UDP不能确保数据的发送和接收顺序。this

下面就来看看UDP协议的工做原理,对UDP的工做原理有一个好的理解,对后面介绍的UDP编程也是一个好的基础。spa

1.1 UDP的工做原理.net

  UDP将网络数据流量压缩成数据报的形式,每个数据报用8个字节(8 X 8位=64位)描述报头信息,剩余字节包含具体的传输数据。UDP报头(只有8个字节)至关于TCP的报头(至少20个字节)很短,UDP报头由4个域组成,每一个域各占2个字节,具体为源端口、目的端口、用户数据报长度和校验和。线程

  具体结构见下图(下面也贴出了TCP报文的结构图,与UDP数据报作一个对比的做用):

UDP协议和TCP协议都使用端口号为不一样的应用保留其各自的数据传输通道这一机制,数据发送方将UDP数据报经过源端口发送出去,而数据接收方则经过目标端口接收数据。

  

1.2 UDP的优点

  前面介绍中说UDP相对于TCP是不可靠的,不能保证有序传输的传输协议,然而UDP协议相对于TCP协议的优点在哪里呢?,

  UDP相对于TCP的优点主要有三个方面的:

      (1)UDP速度比TCP快。

          因为UDP不须要先与对方创建链接,也不须要传输确认,所以其数据的传输速度比TCP快不少。对于一些着重传输性能而不是传输完整性的应用(网络音频播放、视频点播和网络会议等),使用UDP协议更加适合,由于它传输速度快,使经过网络播放的视频音质好、画面清晰。

     (2)UDP有消息边界。

     经过UDP协议进行传输的发送方对应用程序交下来的报文,在添加首部后就向下直接交付给IP层。既不拆分也不合并,而是保留这些报文的边界,因此使用UDP协议不须要像TCP那样考虑消息边界的问题,这样就使得UDP编程相对于TCP在接收到的数据处理方面要简单的多。(对于TCP消息边界的问题能够查看相关的文档,在这里我就不列出来了)

     (3)UDP能够一对多传输

     因为传输数据部创建链接,也就不须要维护链接状态,所以一台服务器能够同时向多个客户端发送相同的信息。利用UDP可使用广播或者组播的方式同时向子网的全部客户端进程发送信息,广播和组播的介绍放到后面TCP编程中介绍。

 上面介绍了UDP协议相对于TCP协议的优点,其中速度快是UDP的最重要的优点,也是像一些网络会议、即时通讯软件传输层选择UDP协议进行传输的缘由所在。

2、.net平台对UDP编程的支持

  介绍完UDP相对于TCP的优点后,固然很但愿在.net平台下开发一个基于UDP协议的一个应用了,而后.net平台下对UDP编程也作了很好的支持,为咱们开发基于UDP协议的网络应用提供不少方便之处,下面就简单介绍.net平台下对UDP编程的支持(主要介绍提供的类来对UDP协议进行编程)。

  .net类库中的UdpClient类对基础的Socket进行了封装,这样就在发送和接受数据时不须要考虑底层套接字的收发时处理的一些细节问题,这样为UDP编程提供了方便,也能够提升开发效率(感受net就是作这样的事情的,对一些底层的实现进行封装,方便咱们的调用,这也体现了面向对象语言的封装特性)对于这个的具体的使用我就不作过多的介绍的,在后面的UDP编程的实现部分将会对该类中主要方法的使用,你们能够查看MSDN来查看该类中其余成员的使用: http://msdn.microsoft.com/zh-cn/library/System.Net.Sockets.UdpClient.aspx

3、UDP编程的具体实现

   因为UDP进程在通讯以前是不须要创建链接,消息接收方可能并不知道是谁给它发的消息,所以UDP编程分为两种模式:一种“实名发送”,即接收方能够由收到的消息得知发送方进程端口,另一种则为“匿名发送”,即接收方并不知道发给它信息的远程进程究竟来自哪一个端口。下面经过一个winform 程序来演示下UDP的编程:

实现代码:

  1 using System;
  2 using System.Net;
  3 using System.Net.Sockets;
  4 using System.Text;
  5 using System.Threading;
  6 using System.Windows.Forms;
  7 
  8 namespace UDPClient
  9 {
 10     public partial class frmUdp : Form
 11     {
 12         private UdpClient sendUdpClient;
 13         private UdpClient receiveUpdClient;
 14         public frmUdp()
 15         {
 16             InitializeComponent();
 17             IPAddress[] ips = Dns.GetHostAddresses("");
 18             tbxlocalip.Text = ips[3].ToString();
 19             int port = 51883;
 20             tbxlocalPort.Text = port.ToString();
 21             tbxSendtoIp.Text = ips[3].ToString();
 22             tbxSendtoport.Text = port.ToString();
 23         }
 24 
 25         // 接受消息
 26         private void btnReceive_Click(object sender, EventArgs e)
 27         {
 28             // 建立接收套接字
 29             IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
 30             IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalPort.Text));
 31             receiveUpdClient = new UdpClient(localIpEndPoint);
 32 
 33 
 34             Thread receiveThread = new Thread(ReceiveMessage);
 35             receiveThread.Start();
 36         }
 37 
 38         // 接收消息方法
 39         private void ReceiveMessage()
 40         {
 41             IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
 42             while (true)
 43             {
 44                 try
 45                 {
 46                     // 关闭receiveUdpClient时此时会产生异常
 47                     byte[] receiveBytes = receiveUpdClient.Receive(ref remoteIpEndPoint);
 48 
 49                     string message = Encoding.Unicode.GetString(receiveBytes);
 50 
 51                     // 显示消息内容
 52                     ShowMessageforView(lstbxMessageView, string.Format("{0}[{1}]", remoteIpEndPoint, message));
 53                 }
 54                 catch
 55                 {
 56                     break;
 57                 }
 58             }
 59         }
 60 
 61         // 利用委托回调机制实现界面上消息内容显示
 62         delegate void ShowMessageforViewCallBack(ListBox listbox, string text);
 63         private void ShowMessageforView(ListBox listbox, string text)
 64         {
 65             if (listbox.InvokeRequired)
 66             {
 67                 ShowMessageforViewCallBack showMessageforViewCallback = ShowMessageforView;
 68                 listbox.Invoke(showMessageforViewCallback, new object[] { listbox, text });
 69             }
 70             else
 71             {
 72                 lstbxMessageView.Items.Add(text);
 73                 lstbxMessageView.SelectedIndex = lstbxMessageView.Items.Count - 1;
 74                 lstbxMessageView.ClearSelected();
 75             }
 76         }
 77         private void btnSend_Click(object sender, EventArgs e)
 78         {
 79             if (tbxMessageSend.Text == string.Empty)
 80             {
 81                 MessageBox.Show("发送内容不能为空","提示");
 82                 return;
 83             }
 84 
 85             // 选择发送模式
 86             if (chkbxAnonymous.Checked == true)
 87             {
 88                 // 匿名模式(套接字绑定的端口由系统随机分配)
 89                 sendUdpClient = new UdpClient(0);
 90             }
 91             else
 92             {
 93                 // 实名模式(套接字绑定到本地指定的端口)
 94                 IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
 95                 IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalPort.Text));
 96                 sendUdpClient = new UdpClient(localIpEndPoint);
 97             }
 98 
 99             Thread sendThread = new Thread(SendMessage);
100             sendThread.Start(tbxMessageSend.Text);
101         }
102 
103         // 发送消息方法
104         private void SendMessage(object obj)
105         {
106             string message = (string)obj;
107             byte[] sendbytes = Encoding.Unicode.GetBytes(message);
108             IPAddress remoteIp = IPAddress.Parse(tbxSendtoIp.Text);
109             IPEndPoint remoteIpEndPoint = new IPEndPoint(remoteIp, int.Parse(tbxSendtoport.Text));
110             sendUdpClient.Send(sendbytes, sendbytes.Length, remoteIpEndPoint);
111           
112             sendUdpClient.Close();
113            
114             // 清空发送消息框
115             ResetMessageText(tbxMessageSend);
116         }
117 
118         // 采用了回调机制
119         // 使用委托实现跨线程界面的操做方式
120         delegate void ResetMessageCallback(TextBox textbox);
121         private void ResetMessageText(TextBox textbox)
122         {
123             // Control.InvokeRequired属性表明
124             // 若是控件的处理与调用线程在不一样线程上建立的,则为true,不然为false
125             if (textbox.InvokeRequired)
126             {
127                 ResetMessageCallback resetMessagecallback = ResetMessageText;
128                 textbox.Invoke(resetMessagecallback, new object[] { textbox });
129             }
130             else
131             {
132                 textbox.Clear();
133                 textbox.Focus();
134             }
135         }
136 
137         // 中止接收
138         private void btnStop_Click(object sender, EventArgs e)
139         {
140             receiveUpdClient.Close();
141         }
142 
143         // 清空接受消息框
144         private void btnClear_Click(object sender, EventArgs e)
145         {
146             this.lstbxMessageView.Items.Clear();
147         }
148     }
149 }

运行结果:

实名发送:

在本地运行本程序的三个进程(分别为A,B,C),把进程C作为接受进程,进程A和进程B都向进程C发信息,进程A和进程分别绑定端口号为11883和21883,发送到端口都为51883,配置界面以下:

首先不勾选“匿名”复选框,在进程C中点击“接收”按钮开启接受线程,在A进程和B进程中发送消息框里分别输入你好,我是1和你好,我是2 ,而后点击发送按钮,此时在进程中就能够看到进程A和进程B发来的消息,以下图:

从图中能够看出每条消息以前都显示了消息的准确来源(包括消息进程锁在的Ip地址和端口号)

匿名发送:

下面把“匿名”复选框勾上后,再按照前面的步骤将获得下面的结果:

  从图中结果能够看出此时列表中显示的消息来源的进程端口号分别为49439和49440,而不是发送消息进程的真实端口(11883和21883)

  这种UDP只能辨别消息源主机的Ip地址,而没法知道发消息的进程到底是哪一个端口称为“匿名发送”。正如咱们平时发手机短信同样,若是咱们把认识的名字和电话号码预先存在通信录里,当一发来信息,接受方立刻就能够历来电显示中看到是谁发来的(实名模式);可是若是是陌生人发来信息或者广告等信息时,仅看来电显示,根本不知道对方是谁(匿名模式),QQ发消息也是同样的道理。

4、UDP广播和组播

  前面UDP的实现中发送数据使用的都是一对一(单播)的通讯方式,即只将数据发送到某一个进程。前面提到UDP能够实现一对多的传输方式,即经过广播和组播把数据发送给一组进程。下面就介绍下UDP广播和组播的相关知识。

4.1 广播和组播的基本概念

  虽然利用TCP协议能够保证数据的可靠、有序的传输,可是TCP仅支持一对一的传输,并且传输时须要在发送端和每个接受端之间创建单独的数据通讯通道,若是须要实现网络会议、网络视频的点播等功能时要向大量主机发送相同的数据包,若是采用单播方式逐个节点传输的话,将会给发送方带来网络堵塞等问题,此时能够考虑实现UDP的多播方式——即广播和组播来实现这样的功能(一对多通讯分为广播和组播两种形式)。

  广播是指同时向子网中的多台计算机发送消息,而且全部子网中的计算机均可以接收到发送方发来的消息,每一个广播消息包含一个特殊的IP地址,这个IP的中子网内主机标志部分的二进制都为1,例如,子网掩码为255.255.255.0,对于子网192.168.0,则这个IP地址为192.168.0.255.

  而后广播消息又分为本地广播和全球广播两种类型, 本地广播是指向子网中的全部计算机发送广播消息,其余网络不会受到本地广播的影响。

IP地址分为两部分——网络标志部分和主机标志部分,这两部分是靠子网掩码来区分的,主机标记部分二进制所有为1的地址成为本地广播地址。例如:A类网络192.168.0.0,使用子网掩码255.255.0.0,则本地广播地址为:
                      

  对于IPv4来讲,全球广播使用全部位全为1的IP地址,即255.255.255.255,这个广播地址表明数据报的目的地是网络上全部设备,可是因为路由器会自动过滤全球广播,因此使用这个地址根本就没有任何意义。

  而后当接收者分布于多个不一样的子网时,广播将再也不适用,此时能够经过组播的方式来实现,组播也叫多路广播,组播是将信息从一台计算机发送到本网或全网内指定的计算机上,即发送到那些加入了指定组播组的计算机上,每台计算机均可以经过程序随时加入某个组播组中,也能够随时退出来, 就像咱们开网络会议同样,能够随时加入会议室进行开会,会议结束和会议进行中均可以随意的退出来。

4.2 加入和退出组播组

  组播组又称为多路广播组,组播地址的范围在224.0.0.0到239.255.255.255的D类IP地址(至于这个概念你们能够百度百科里面就查看)。任何发送到组播地址的消息都会被发送到组内全部成员设备上,组可使永久的也能够是临时,大多数咱们使用的都是临时的,仅在有成员的时候才存在。

  使用组播时,注意生命周期(TTL,Time to live)的设,TTL值表示容许路由器转发的最大次数,当达到这个最大值时,数据包就会被丢弃,TTL的默认值为1,设置为1时代表只能在子网中发送数据。

加入组播组:

  UdpClient类提供了JoinMulticastGroup方法,用于将UdpClient加入到使用指定的IPAddress的组播组中,调用该方法后,基础的Socket会自动向路由器发送数据包,用于请求成为组播组的成员,若是成为组播组成员,就能够接收该组播组的数据报。至于具体方法的时候会在后面实现UDP广播程序中会用到,另外你们也能够查看MSDN,因此这里我就再也不列出来了,只是指出这个方法的做用,让你们知道有这么个方法来调用。

退出组播组:

  一样利用UdpClient的DropMulticastGroup方法,能够退出组播组,调用该方法后,基础Socket会自动向路由器发送数据包,用于请求从指定的组播组里退出,从组中回收UdpClient对象以后,将再也不接受发送到该组播组的数据报。

5、总结

   因为时间的关系,这篇文章就介绍到这里的,至于实现UDP广播的程序放在后面一个专题里面的,前面也对广播和组播的概念进行了简单的介绍,相信你们也对广播和组播有了个简单的认识(广播组和组播组说白了就是一个IP地址的集合,其实实现UDP广播的程序和前面实现单播的程序差很少,只是前面绑定了一个IP地址固然也只能发送到一个IP地址了,也就是所谓的单播,多播和广播就是发送的IP地址是一个组,固然也就实现了一对多的传输了)。UDP广播程序的实现就放在下一个专题和你们分享的,由于我如今要去吃饭了,吃完饭再继续和你们介绍,但愿你们若是以为有帮助的话,也能够推荐下,这给我继续写下去的动力,谢谢你们的支持

 本专题源码地址:http://files.cnblogs.com/zhili/UDPCommunication.zip 

转自:http://www.cnblogs.com/zhili/archive/2012/09/01/UDP_Multicast.html

相关文章
相关标签/搜索