[C#网络编程系列]专题六:UDP编程(转载:)引用:前一个专题简单介绍了TCP编程的一些知识,UDP与TCP地位相当的另一个传输层协议,它也是当下流行的很多主流网络应用(例如QQ、MSN和Skype等一些即时通信软件传输层都是应用UDP协议的)底层的传输基础,所以在本专题中就简单介绍下UDP的工作原理和UDP编程的只是,希望可以对刚接触网络编程的朋友起到入门的作用。一、UDP介绍UDP和TCP都是构建在IP层之上传输层的协议,但UDP是一种简单、面向数据报(Sock_Dgram)的无连接协议,提供的是不一定可靠的传输服务。然而TCP是一种面向连接、可靠的,面向字节流(Sock_Stream)的传输协议,对于“无连接”是指在正式通信前不必与对方先建立连接,不管对方状态如何都可以直接发送过去(就如QQ中通过QQ号查看好友后发送添加好友请求,此间不需要考虑对方的状态如何,都照样发送请求)。从UDP和TCP的定义中就可以看出它们两者的区别了,(1)UDP的可靠性不如TCP,因为TCP传输前要首先建立连接,这样就增加了TCP传输的可靠性,所以UDP也被称为不可靠的传输协议,关于TCP的介绍可以看我上一篇博客的介绍。TCP和UDP还有另外一个区别。(2)UDP不能保证有序传输。即UDP不能确保数据的发送和接收顺序。下面就来看看UDP协议的工作原理,对UDP的工作原理有一个好的理解,对后面介绍的UDP编程也是一个好的基础。1.1UDP的工作原理UDP将网络数据流量压缩成数据报的形式,每一个数据报用8个字节(8X8位=64位)描述报头信息,剩余字节包含具体的传输数据。UDP报头(只有8个字节)相当于TCP的报头(至少20个字节)很短,UDP报头由4个域组成,每个域各占2个字节,具体为源端口、目的端口、用户数据报长度和校验和,具体结构见下图(下面也贴出了TCP报文的结构图,与UDP数据报做一个对比的作用):UDP协议和TCP协议都使用端口号为不同的应用保留其各自的数据传输通道这一机制,数据发送方将UDP数据报通过源端口发送出去,而数据接收方则通过目标端口接收数据。1.2UDP的优势前面介绍中说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协议进行传输的原因所在。二、.net平台对UDP编程的支持介绍完UDP相对于TCP的优势后,当然很希望在.net平台下开发一个基于UDP协议的一个应用了,然后.net平台下对UDP编程也做了很好的支持,为我们开发基于UDP协议的网络应用提供很多方便之处,下面就简单介绍.net平台下对UDP编程的支持(主要介绍提供的类来对UDP协议进行编程)。.net类库中的UdpClient类对基础的Socket进行了封装,这样就在发送和接受数据时不需要考虑底层套接字的收发时处理的一些细节问题,这样为UDP编程提供了方便,也可以提高开发效率(感觉net就是做这样的事情的,对一些底层的实现进行封装,方便我们的调用,这也体现了面向对象语言的封装特性)对于这个的具体的使用我就不做过多的介绍的,在后面的UDP编程的实现部分将会对该类中主要方法的使用,大家可以查看MSDN来查看该类中其他成员的使用:三、UDP编程的具体实现由于UDP进程在通信之前是不需要建立连接,消息接收方可能并不知道是谁给它发的消息,因此UDP编程分为两种模式:一种“实名发送”,即接收方可以由收到的消息得知发送方进程端口,另外一种则为“匿名发送”,即接收方并不知道发给它信息的远程进程究竟来自哪个端口。下面通过一个winform程序来演示下UDP的编程:实现代码:usingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Text;usingSystem.Threading;usingSystem.Windows.Forms;namespaceUDPClient{publicpartialclassfrmUdp:Form{privateUdpClientsendUdpClient;privateUdpClientreceiveUpdClient;publicfrmUdp(){InitializeComponent();IPAddress[]ips=Dns.GetHostAddresses();tbxlocalip.Text=ips[3].ToString();intport=51883;tbxlocalPort.Text=port.ToString();tbxSendtoIp.Text=ips[3].ToString();tbxSendtoport.Text=port.ToString();}//接受消息privatevoidbtnReceive_Click(objectsender,EventArgse){//创建接收套接字IPAddresslocalIp=IPAddress.Parse(tbxlocalip.Text);IPEndPointlocalIpEndPoint=newIPEndPoint(localIp,int.Parse(tbxlocalPort.Text));receiveUpdClient=newUdpClient(localIpEndPoint);ThreadreceiveThread=newThread(ReceiveMessage);receiveThread.Start();}//接收消息方法privatevoidReceiveMessage(){IPEndPointremoteIpEndPoint=newIPEndPoint(IPAddress.Any,0);while(true){try{//关闭receiveUdpClient时此时会产生异常byte[]receiveBytes=receiveUpdClient.Receive(refremoteIpEndPoint);stringmessage=Encoding.Unicode.GetString(receiveBytes);//显示消息内容ShowMessageforView(lstbxMessageView,string.Format({0}[{1}],remoteIpEndPoint,message));}catch{break;}}}//利用委托回调机制实现界面上消息内容显示delegatevoidShowMessageforViewCallBack(ListBoxlistbox,stringtext);privatevoidShowMessageforView(ListBoxlistbox,stringtext){if(listbox.InvokeRequired){ShowMessageforViewCallBackshowMessageforViewCallback=ShowMessageforView;listbox.Invoke(showMessageforViewCallback,newobject[]{listbox,text});}else{lstbxMessageView.Items.Add(text);lstbxMessageView.SelectedIndex=lstbxMessageView.Items.Count-1;lstbxMessageView.ClearSelected();}}privatevoidbtnSend_Click(objectsender,EventArgse){if(tbxMessageSend.Text==string.Empty){MessageBox.Show(发送内容不能为空,提示);return;}//选择发送模式if(chkbxAnonymous.Checked==true){//匿名模式(套接字绑定的端口由系统随机分配)sendUdpClient=newUdpClient(0);}else{//实名模式(套接字绑定到本地指定的端口)IPAddresslocalIp=IPAddress.Parse(tbxlocalip.Text);IPEndPointlocalIpEndPoint=newIPEndPoint(localIp,int.Parse(tbxlocalPort.Text));sendUdpClient=newUdpClient(localIpEndPoint);}ThreadsendThread=newThread(SendMessage);sendThread.Start(tbxMessageSend.Text);}//发送消息方法privatevoidSendMessage(objectobj){stringmessage=(string)obj;byte[]sendbytes=Encoding.Unicode.GetBytes(message);IPAddressremoteIp=IPAddress.Parse(tbxSendtoIp.Text);IPEndPointremoteIpEndPoint=newIPEndPoint(remoteIp,int.Parse(tbxSendtoport.Text));sendUdpClient.Send(sendbytes,sendbytes.Length,remoteIpEndPoint);sendUdpClient.Close();//清空发送消息框ResetMessageText(tbxMessageSend);}//采用了回调机制//使用委托实现跨线程界面的操作方式delegatevoidResetMessageCallback(TextBoxtextbox);privatevoidResetMessageText(TextBoxtextbox){//Control.InvokeRequired属性代表//如果控件的处理与调用线程在不同线程上创建的,则为true,否则为falseif(textbox.InvokeRequired){ResetMessageCallbackresetMessagecallback=ResetMessageText;textbox.Invoke(resetMessagecallback,newobject[]{textbox});}else{textbox.Clear();textbox.Focus();