全面掌握VisualC#实现UDP协议

一.走进UDP 协议

UDP(User Datagram Protocol) 协议 就是“用户 数据 协议 ”,它是一种无连接的 协议 ,无连接主要是和TCP 协议 相比较的。我们知道当利用TCP 协议 传送 数据 的时候,首先必须先建立连接(也就是所谓的握手)才可以传输 数据 。而当 计算机 利用UDP 协议 进行 数据 传输的时候,发送方只需要知道对方的IP地址和端口号就可以发送 数据 ,而并不需要进行连接。当然如果你非要进行连接,通过Visual C#也是可以实现的,但前提是要确定连接的远程主机的端口号处于监听状态,否则程序会出现不必要的错误,但这是种画蛇添足的做法,不仅丢失了UDP 协议 的无连接传送 数据 的特点和优点,而且还给程序运行带来了不安定的因素。所以这种方法并不值得提倡。

由于UDP
协议 并不需要进行确定的连接,所以编写基于UDP 协议 的应用程序比起编写基于TCP 协议 的应用程序要简单些(程序中可以不需要考虑连接和一些异常的捕获工作)。但同时也给基于UDP 协议 编写的程序带来了一个致命的缺点,UDP由于不提供可靠 数据 的传输,当 计算机 之间利用UDP 协议 传送 数据 的时候,发送方只管发送 数据 ,而并不确认 数据 是否被对方接收。这样就会导致某些UDP 协议 数据 包在传送的过程中丢失,尤其网络质量不令人满意的情况下,丢失 数据 包的现象会更严重。这就是为什么在网络上传输重要 数据 不采用UDP 协议 的原因。

但是我们也不能因为这一个缺点就全面否定UDP
协议 ,这是因为虽然利用UDP 协议 来传送 安全 性要求高的 数据 是不适合的,但对于那些不重要的 数据 ,或者即使丢失若干 数据 包也不影响整体性的 数据 ,如音频 数据 ,视频 数据 等,采用UDP 协议 就是一个非常不错的选择。如目前网络流行的很多即时聊天程序,如OICQ和ICQ等,采用的就是UDP 协议 。同时虽然UDP 协议 无法保证 数据 可靠性,但具有对网络资源开销较小, 数据 处理速度快的优点,所以在有些对 数据 安全 性要求不是很高的情况下,采用UDP 协议 也是一个非常不错的选择。

总结一下上面的内容,可见UDP是一种不面向连接的网络
协议 ,既有其优点,也有其不足,具体如下:

1.基于UDP
协议 的网络应用程序,实现起来比较简单,并且基于UDP 协议 的网络应用程序在运行时,由于受到环境影响较小,所以不容易出错。

2.UDP
协议 占用网络资源较少, 数据 处理较快,所以在网络中传送对 安全 性要求不是十分高 数据 时,其优点比较明显。所谓对 安全 性要求不高的 数据 ,是指那些不重要的 数据 ,或者是即使丢失若干 数据 ,也不影响其整体的 数据 ,如音频 数据 等。目前很多流行的网络应用程序都是基于UDP 协议 的,如OICQ、ICQ等。

3.由于其不是面向连接的网络
协议 ,其缺点也是非常明显的,有些时候甚至是致命的。因为使用UDP 协议 来传送 数据 ,在 数据 发送后,在发送方并不确认对方是否接收到。这样就可能导致传送的 数据 在网络中丢失,尤其在网络条件并不很好的情况下,丢失 数据 包的现象就更多。所以传送重要 数据 一般不采用UDP 协议

二.简介Visual C#发送、接收UDP数据包使用的主要类及其用法:

用Visual C#实现UDP 协议 ,最为常用,也是最为关键的类就是UdpClient,UdpClient位于命名空间System.Net.Sockets中,Visual C#发送、接收UDP 数据 包都是通过UdpClient类的。表01和表02是UdpClient类中常用方法和属性及其简要说明。

方法 说明
Close 关闭 UDP 连接
Connect 建立与远程主机的连接
DropMulticastGroup 退出多路广播组
JoinMulticastGroup 将 UdpClient 添加到多路广播组
Receive 返回已由远程主机发送的 UDP 数据文报
Send 将 UDP 数据文报发送到远程主机


表01:UdpClient类中常用方法及其说明。

属性 说明
Active 获取或设置一个值,该值指示是否已建立了与远程主机的连接
Client 获取或设置基础网络套接字
表02:UdpClient类中常用方法及其说明。

1.Visual C#使用UdpClient类发送UDP 数据包:

在具体使用中,一般分成二种情况:

(1). 知道远程 计算机IP地址:

"Send"方法的调用语法如下:

public int Send ( byte[] dgram , int bytes , IPEndPoint endPoint ) ;


参数说明:

dgram 要发送的 UDP 数据文报(以字节数组表示)。

bytes 数据文报中的字节数。

endPoint 一个 IPEndPoint,它表示要将 数据文报发送到的主机和端口。

返回值 已发送的字节数。

下面使用UdpClient发送UDP 数据包的具体的调用例子:

IPAddress HostIP = new IPAddress.Parse ( "远程计算机IP地址" ) ; IPEndPoint host = new IPEndPoint ( HostIP , 8080 ) ; UdpClient.Send ( "发送的字节" , "发送的字节长度" , host ) ;


(2). 知道远程 计算机名称::

知道远程 计算机名称后,利用"Send"方法直接把UDP 数据包发送到远程主机的指定端口号上了,这种调用方式也是最容易的,语法如下:

public int Send ( byte[ ] dgram , int bytes , string hostname , int port ) ;


参数说明:

dgram 要发送的 UDP 数据文报(以字节数组表示)。

bytes 数据文报中的字节数。

hostname 要连接到的远程主机的名称。

port 要与其通讯的远程端口号。

返回值 已发送的字节数。

2.Visual C#使用UdpClient类接收UDP 数据包:

接收UDP 数据包使用的是UdpClient中的“Receive”方法。此方法的调用语法如下:

public byte [] Receive ( ref IPEndPoint remoteEP ) ;


参数

remoteEP 是一个 IPEndPoint类的实例,它表示网络中发送此 数据包的节点。

如果指定了远程 计算机要发送到本地机的端口号,也可以通过侦听本地端口号来实现对 数据的获取,下面就是通过侦听本地端口号“8080”来获取信息代码:

server = new UdpClient ( ) ; receivePoint = new IPEndPoint (new IPAddress ( "127.0.0.1" ) , 8080 ) ; byte[] recData = server.Receive ( ref receivePoint ) ;


三.Visual C#实现UDP协议之网络对时系统的体系结构及功能简介:

在局域网中有很多应用软件为了协同工作,需要保证客户机上时间统一,而为了实现这一点,通常的做法是客户机从一个时间相对正确的服务器读取时间,以此来校正本地时间。如经常看到的GPS对时系统等。本节编写的局域网上对时系统的主要的功能是保证局域网上 计算机时间、日期的统一。网络对时程序是体系结构分成服务器端程序和客户端程序二个部分,具体的作法是:在同一个网段上,固定一台 计算机作为对时的服务器,在这个网段的所有 计算机都可以读取这台服务器上的时间和日期,并依此服务器上的时间和日期为基准,来确定本地的时间和日期。在服务器端程序需要达到以下功能:

能够接收局域网中任一台客户机的请求

记录请求客户机的 计算机名称,和请求时间

准确发送服务器端的时间和日期

端程序要达到以下功能:

能够设定服务器的主机或者IP地址

能够接收服务器端发送的时间、日期信息

能够以接收的服务器端时间、日期为基准,校正本地时间

因此在具体用Visual C#实现网络对时系统时就包括二个部分:服务器端程序和客户端程序。下面首先介绍Visual C#实现网络对时系统中服务器端程序的具体步骤。

四.Visual C#实现网络对时系统之服务器端程序的具体步骤:

服务器端程序比客户端程序相对要简单,主要因是服务器端程序的工作比较简单,就

是接收客户端的对时请求、发送服务器端的时间 数据。而于客户端不仅要传送和接收 数据,还要把服务器端的时间提取出来,并以此来修改本地 计算机的时间、日期。下面是用Visual C#实现网络对时系统之服务器端程序的具体步骤步骤。

1.启动Visual Studio .Net。

2.选择菜单【文件】|【新建】|【项目】后,弹出【新建项目】对话框。

3.将【项目类型】设置为【Visual C#项目】。

4.将【模板】设置为【Windows应用程序】。

5.在【名称】文本框中输入【UDP对时服务器端】。

6.在【位置】的文本框中输入【E:/VS.NET项目】,然后单击【确定】按钮

7.在【解决方案资源管理器】窗口中,双击Form1.cs文件,进入Form1.cs文件的编辑界面。

8.在Form1.cs文件的开头,用下列导入命名空间代码替代系统缺省的导入命名空间代码。

using System ; using System.Drawing ; using System.Collections ;

using System.ComponentModel ; using System.Windows.Forms ;

using System.Data ; using System.Net ;

using System.Net.Sockets ;

using System.Threading ; //程序中使用到线程

using System.Text ; //程序中使用到编码



9.切换到【Form1.cs( 设计)】窗口,并从【工具箱】中的【Windows窗体组件】中往窗体中拖入下列组件,并执行相应操作:

一个Label组件,显示对时服务器正在运行信息

一个ListBox组件,名称为listBox1,用以显示客户端和服务器端交流的 日志

一个Button组件,名称为button1,并在其拖入窗体后,双击,则系统会在Form1.cs文件中产生其Click事件对应的处理代码。

10.在【解决方案资源管理器】窗口中,双击Form1.cs文件,进入Form1.cs文件的编辑界面。在Form1.cs中的class代码区添加下列代码,下列代码是定义程序中使用的全局变量和创建全局使用的实例:

private UdpClient server ;

private IPEndPoint receivePoint ;

private int port = 8080 ; //定义端口号

 private int ip = 127001 ; //设定本地IP地址 private Thread startServer ;



11.以下面代码替代系统产生的InitializeComponent过程。

private void InitializeComponent ( )

{ this.listBox1 = new System.Windows.Forms.ListBox ( ) ; this.label1 = new System.Windows.Forms.Label ( ) ; this.button1 = new System.Windows.Forms.Button ( ) ; this.SuspendLayout ( ) ; this.listBox1.ItemHeight = 12 ; this.listBox1.Location = new System.Drawing.Point ( 14 , 40 ) ; this.listBox1.Name = "listBox1" ; this.listBox1.Size = new System.Drawing.Size ( 268 , 220 ) ; this.listBox1.TabIndex = 0 ; this.label1.ForeColor = System.Drawing.Color.Red ; this.label1.Location = new System.Drawing.Point ( 44 , 10 ) ; this.label1.Name = "label1" ; this.label1.Size = new System.Drawing.Size ( 210 , 24 ) ; this.label1.TabIndex = 1 ; this.label1.Text = "UDP对时服务器端正在运行......" ; this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat ; this.button1.Location = new System.Drawing.Point ( 106 , 278 ) ; his.button1.Name = "button1" ; this.button1.Size = new System.Drawing.Size ( 75 , 34 ) ; this.button1.TabIndex = 2 ; this.button1.Text = "清除信息" ; this.button1.Click += new System.EventHandler ( this.button1_Click ) ; this.AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ; this.ClientSize = new System.Drawing.Size ( 300 , 329 ) ; this.Controls.AddRange ( new System.Windows.Forms.Control[] { this.button1 , this.listBox1 , this.label1} ) ; this.MaximizeBox = false ; this.Name = "Form1" ; this.Text = "UDP对时服务器端" ; this.Load += new System.EventHandler ( this.Form1_Load ) ; this.ResumeLayout ( false ) ;

}



至此,【UDP对时服务器端】项目的界面 设计和功能实现的前期工作就完成了, 设计界面如图01所示:



图01:【UDP对时服务器端】项目的 设计界面

12.在Form1.cs文件中的InitializeComponent过程的后面添加下面代码,下列代码是定义过程“start_server”。此过程的功能是获取客户端对时请求 数据,并向客户端发送服务器当前时间和日期。

public void start_server ( ) { while ( true ) { //接收从远程主机发送到本地8080端口的数据 byte[] recData = server.Receive ( ref receivePoint ) ; ASCIIEncoding encode = new ASCIIEncoding ( ) ; //获得客户端请求数据 string Read_str = encode.GetString ( recData ) ; //提取客户端的信息,存放到定义为temp的字符串数组中 string[] temp = Read_str.Split ( "/".ToCharArray ( ) ) ; //显示端口号的请求信息 listBox1.Items.Add ( "时间:"+ DateTime.Now. ToLongTimeString ( ) + " 接收信息如下:" ) ; listBox1.Items.Add ( "客户机:" + temp[0] ) ; listBox1.Items.Add ( "端口号:" + temp[1] ) ; //发送服务器端时间和日期 byte[] sendData =encode.GetBytes ( System.DateTime.Now.ToString ( ) ) ; listBox1.Items.Add ( "发送服务器时间!" ) ; //对远程主机的指定端口号发送服务器时间 server.Send ( sendData , sendData.Length , temp[0] , Int32.Parse ( temp[1] ) ) ; } }


请注意:上述代码中约定客户机程序发送对时请求信息到服务器的8080端口号。服务器端程序接收发送到本地8080端口号的 数据就完成了 数据接收。为了能够让服务器端程序知道是那台客户机提出请求和要把对时信息发送到客户机的那个端口号上,客户端程序对发送的对时请求信息进行了 设计。客户端的对时请求信息结构为:

计算机名称 + / + 客户机接收信息端口号

这样如果客户端 计算机名称为:majinhu,接收服务器端时间 数据的端口号是8080,则客户端程序发送的对时请求 数据就为:majinhu/8080。

服务器端程序在接收到客户端对时请求 数据,并进行分析后,就能够通过UdpClient类的Send方法准确的把服务器端当前的时间和日期发送到客户端指定的端口号上。这样客户端程序通过读取指定的端口号,就能够获得服务器端当前的时间和日期,从而以此来修正客户端的时间和日期了。

13.在“start_server”过程之后面添加下面代码,下列代码是定义“run”过程。“run”过程的作用是创建一个线程实例,并以“start_server”过程来初始化线程实例。之所以采用线程是因为服务器端程序需要不间断读取发送到8080端口号,并且Receive方法是一个阻塞式方法。采用线程就是为了保证服务器端程序正常运行:

public void run ( ) { //利用本地8080端口号来初始化一个UDP网络服务 server = new UdpClient ( port ) ; receivePoint = new IPEndPoint ( new IPAddress ( ip ) , port ) ; //开一个线程 startServer = new Thread ( new ThreadStart ( start_server ) ) ; //启动线程 startServer.Start ( ) ; }


14.在Form1.cs中的Main 函数之后添加下列代码,下列代码是定义“Form1_Load”事件,在此事件中将调用“run”过程,这样当服务器端程序运行后,就启动网络对时服务:

private void Form1_Load ( object sender , System.EventArgs e ) { //启动对时服务 run ( ) ; }


15.在Form1.cs文件中的“Form1_Load”事件之后,添加下列代码,下列代码是定义button1的“Click”事件,此事件的作用是清除服务器端程序显示的 日志信息:

private void button1_Click ( object sender , System.EventArgs e ) { //清除服务器端程序日志 listBox1.Items.Clear ( ) ; }


16.用下列代码替换Form1.cs中的Dispose方法。下列代码的功能是手动收集程序中使用的资源:

protected override void Dispose ( bool disposing ) { try { //关闭线程 startServer.Abort ( ) ; //清除资源 server.Close ( ) ; } catch { } ; if ( disposing ) { if ( components != null ) { components.Dispose ( ) ; } } base.Dispose ( disposing ) ; }



至此,在上述步骤都正确完成,【UDP对时服务器端】项目的全部工作就完成了。图02【UDP对时服务器端】运行后的界面,在日志信息中记录了对时请求客户机的名称,发送对时数据的端口号以及客户端请求的时间:



图02:【UDP对时服务器端】项目的运行界面

五.Visual C#实现网络对时系统之客户端程序的具体步骤:

客户端的程序比服务器端程序要复杂些,因为客户端程序不仅要往服务器端发送对时请求信息,接收服务器端反馈的日期和时间数据,还要提取这些时间和日期信息,并以此来修改本地端的日期和时间。参照上面实现网络对时系统服务器端程序,实现请求信息的发送和接收应相对要容易许多。所以客户端程序的关键就是根据获得的服务器端日期、时间数据来修改本地的日期、时间。在.Net FrameWork SDK 3705版本中并没有提供修改本地日期和时间的函数和类库,本文的实现办法是引入WinAPI函数,通过对应的WinAPI函数来更正本地时间和日期的,具体可参阅以下第十三和第十八步。

以下是Visual C#实现网络对时系统之客户端程序的具体实现步骤:

1.启动Visual Studio .Net。

2.选择菜单【文件】|【新建】|【项目】后,弹出【新建项目】对话框。

3.将【项目类型】设置为【Visual C#项目】。

4.将【模板】设置为【Windows应用程序】。

5.在【名称】文本框中输入【UDP对时客户端】。

6.在【位置】的文本框中输入【E:/VS.NET项目】,然后单击【确定】按钮。

7.【解决方案资源管理器】窗口中,双击Form1.cs文件,进入Form1.cs文件的编辑界面。

8.在Form1.cs文件的开头,用下列导入命名空间代码替换Form1.cs中缺省的导入命名空间代码。

     
     using System.Collections ;
using System.ComponentModel ;
using System.Windows.Forms ;
using System.Data ;
using System.Net ;
using System.Net.Sockets ;
using System.Runtime.InteropServices ;
//程序引入WinAPI函数要使用到


9.把Visual Studio .Net的集成开发环境的当前窗口切换到【Form1.cs( 设计)】窗体设计界面,并从【工具箱】中的【Windows窗体组件】中往窗体中拖入下列组件,并执行相应操作:

三个TextBox组件,分别用来输入服务器的IP地址,和显示本地时间、服务器的时间

二个Button组件,分别是button1和button2,在 设计界面中分别双击button1和button2,系统会自动产生它们对应的Click事件处理代码。

三干个Label组件。

10.【解决方案资源管理器】窗口中,双击Form1.cs文件,进入Form1.cs文件的编辑界面。在Form1.cs中的namespace代码区添加下列代码,下列代码的功能是在程序中定义系统时间的结构体。

     
     [ StructLayout (  LayoutKind.Sequential  )]
public class SystemTime 
{
	public short year ;
	public short Month ; 
	public short DayOfWeek ; 
	public short Day ;
	public short Hour ; 
	public short Minute ; 
	public short Second ; 
	public short Milliseconds ;
}
//定义系统时间的结构


11.在Form1.cs中的class代码区添加下列代码,下列代码的功能是定义程序中使用的全局变量。

     
     private UdpClient client ;
//创建UDP网络服务
private IPEndPoint receivePoint ;
private int port = 8080 ; 
//定义接收服务器端程序发送对时信息对应的端口号
private string timeString = DateTime.Now.ToString  ( ) ;
//存放时间日期信息字符串
private DateTime temp ;
//定义一个时间类型,用以修改当前时间和日期

12.并以下面代码替换Form.cs中由系统产生的InitializeComponent过程。


     
     private void InitializeComponent ( )
{
this.button1 = new System.Windows.Forms.Button ( ) ;
this.button2 = new System.Windows.Forms.Button ( ) ;
this.textBox1 = new System.Windows.Forms.TextBox ( ) ;
this.textBox2 = new System.Windows.Forms.TextBox ( ) ;
this.label1 = new System.Windows.Forms.Label ( ) ;
this.label2 = new System.Windows.Forms.Label ( ) ;
this.label3 = new System.Windows.Forms.Label ( ) ;
this.textBox3 = new System.Windows.Forms.TextBox ( ) ;
this.SuspendLayout ( ) ;
this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat ;
this.button1.Location = new System.Drawing.Point ( 128 , 128 ) ;
this.button1.Name = "button1" ;
this.button1.Size = new System.Drawing.Size ( 112 , 40 ) ;
this.button1.TabIndex = 0 ;
this.button1.Text = "获取" ;
this.button1.Click += new System.EventHandler ( this.button1_Click ) ;
this.button2.FlatStyle = System.Windows.Forms.FlatStyle.Flat ;
this.button2.Location = new System.Drawing.Point ( 128 , 184 ) ;
this.button2.Name = "button2" ;
this.button2.Size = new System.Drawing.Size ( 112 , 40 ) ;
this.button2.TabIndex = 1 ;
this.button2.Text = "对时" ;
this.button2.Click += new System.EventHandler ( this.button2_Click ) ;
this.textBox1.Location = new System.Drawing.Point ( 120 , 56 ) ;
this.textBox1.Name = "textBox1" ;
this.textBox1.Size = new System.Drawing.Size ( 200 , 21 ) ;
this.textBox1.TabIndex = 2 ;
this.textBox1.Text = "" ;
this.textBox2.Location = new System.Drawing.Point ( 120 , 88 ) ;
this.textBox2.Name = "textBox2" ;
this.textBox2.Size = new System.Drawing.Size ( 200 , 21 ) ;
this.textBox2.TabIndex = 3 ;
this.textBox2.Text = "" ;
this.label1.Location = new System.Drawing.Point ( 48 , 56 ) ;
this.label1.Name = "label1" ;
this.label1.TabIndex = 4 ;
this.label1.Text = "本地时间:" ;
this.label2.Location = new System.Drawing.Point ( 40 , 88 ) ;
this.label2.Name = "label2" ;
this.label2.Size = new System.Drawing.Size ( 88 , 24 ) ;
this.label2.TabIndex = 5 ;
this.label2.Text = "服务器时间:" ;
this.label3.Location = new System.Drawing.Point ( 16 , 24 ) ;
this.label3.Name = "label3" ;
this.label3.Size = new System.Drawing.Size ( 112 , 23 ) ;
this.label3.TabIndex = 6 ;
this.label3.Text = "设定服务器地址:" ;
this.textBox3.Location = new System.Drawing.Point ( 120 , 24 ) ;
this.textBox3.Name = "textBox3" ;
this.textBox3.Size = new System.Drawing.Size ( 200 , 21 ) ;
this.textBox3.TabIndex = 7 ;
this.textBox3.Text = "" ;
this.AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ;
this.ClientSize = new System.Drawing.Size ( 352 , 245 ) ;
this.Controls.AddRange ( new System.Windows.Forms.Control[] {
	  this.textBox3 ,
	  this.textBox2 ,
	  this.textBox1 ,
	  this.button2 ,
	  this.button1 ,
	  this.label1 ,
	  this.label2 ,
	  this.label3} ) ;
this.MaximizeBox = false ;
this.Name = "Form1" ;
this.Text = "UDP对时客户端" ;
this.ResumeLayout ( false ) ;
}


至此【UDP对时客户端】项目的界面设计和程序功能实现的前期工作就基本完成了,其设计界面如图03所示:



图03:【UDP对时客户端】项目的 设计界面


13.在Form1.cs中的InitializeComponent过程之后,添加下列代码,下列代码的功能是在程序中导入WinAPI 函数——SetSystemTime,这个函数位于文件Kernel32.dll。程序就是通过此函数来更正系统时间的。

     
     [ DllImport (  "Kernel32.dll"  )]
private static extern bool SetSystemTime (  SystemTime time ) ;
//引入API函数


14.并把它添加到在导入WinAPI 函数代码之后,再添加下列代码,下列代码是定义“start_client”过程。此过程的功能是向服务器端传送对时请求,并获取从服务器端反馈来的时间日期数据。

     
     {
	client = new UdpClient ( port ) ;
	IPAddress a = IPAddress.Parse  ( "127001" ) ;
	receivePoint = new IPEndPoint ( a  , port ) ;
	IPAddress HostIP ;
	bool continueLoop = true ;
	while ( continueLoop )
	{
	string hostName = Dns.GetHostName ( ) ;
	System.Text.ASCIIEncoding encode 
          = new System.Text.ASCIIEncoding ( ) ;
	//定义发送到服务器端的请求信息
	//请求信息是一个字符串,为客户端名称和接收服务器反馈信息的端口号组成的字符串
	string sendString = hostName + "/" + port.ToString ( ) ;
	byte[] sendData = encode.GetBytes ( sendString ) ;
	//判断使用者输入的是IP地址还是计算机名称
	try
	{
		HostIP = IPAddress.Parse  ( textBox3.Text ) ;
	}
	catch
	{
		//如果输入的是计算机名称,则按照执行下列代码。
		//发送请求信息
		client.Send ( sendData , sendData.
                   Length , textBox3.Text , 8080 ) ;
		//接收来自服务器端的信息
			byte[] recData = 
                           client.Receive ( ref receivePoint ) ;
		timeString = encode.GetString ( recData ) ;
		client.Close ( ) ;
		continueLoop=false ;				
		return ;
	}
	//输入的是IP地址,则执行下列代码
	IPEndPoint host = new IPEndPoint ( HostIP ,8080 ) ;
	//发送请求信息
	client.Send ( sendData , sendData.Length , host ) ;
	//接收来自服务器端的信息
	byte[] recData1 = client.Receive ( ref receivePoint ) ;
	//获取服务器端的时间和日期
	timeString = encode.GetString ( recData1 ) ;
	client.Close ( ) ;
	//退出循环
	continueLoop=false ;				
}
}


如果“start_client”过程正确调用,就把服务器端的时间和日期保存到timeString字符串中了。

17.用下列代码替换Form1.cs中button1的“Click”事件的处理代码。下列代码的功能是调用“start_client”过程,获取并显示服务器端程序的时间和日期信息。

     
     private void button1_Click ( object sender , System.EventArgs e )
{
	start_client ( ) ;
	textBox1.Text = DateTime.Now.ToString ( ) ;
	//显示客户端当前时间和日期
	textBox2.Text = timeString ;
	//显示服务器当前时间和日期
}


18.用下列代码替换Form1.cs中button2的“Click”事件对应的处理代码。下列代码的功能是根据获取的服务器时间和日期数据来更正客户端时间和日期。

     
     private void button2_Click ( object sender , System.EventArgs e )
{
	start_client ( ) ;
	//把接收来的数据转换时间日期格式
	try
	{
		temp = DateTime.Parse  ( timeString ) ;
	}
	catch
	{
		MessageBox.Show  ( "错误时间" ) ;
		return ;
	}
	//根据得到的时间日期,来定义时间、日期
	SystemTime st= new SystemTime ( ) ;
	st.year=  ( short )temp.Year ;
	st.Month= ( short )temp.Month ;
	st.DayOfWeek= ( short )temp.DayOfWeek ;
	st.Day= ( short )temp.Day ;
	st.Hour=Convert.ToInt16 ( temp.Hour ) ;
	if ( st.Hour>=12 )
	{
		st.Hour-= ( short )8 ;
	}
	else if ( st.Hour >= 8 )
	{
		st.Hour-= ( short )8 ;
	}
	else
	{
		st.Hour+= ( short )16 ;
	}
	st.Minute=Convert.ToInt16 ( temp.Minute ) ;
	st.Second=Convert.ToInt16 ( temp.Second ) ;
	st.Milliseconds=Convert.ToInt16 ( temp.Millisecond ) ;
	//修改本地端的时间和日期
	if ( SetSystemTime ( st ) )
	{
	MessageBox.Show ( DateTime.Now.ToString ( ) ,"修改成功" ) ;
	} 
	else
	MessageBox.Show ( "不成功!" ,"不成功" ) ;
}



至此,在正确完成上述步骤,全部保存后,【网络对时客户端】项目的全部工作就完成了。

六.运行基于UDP协议网络对时系统,实现网络对时:

首先要确认确认网络对时系统中的服务器端程序已经运行和其IP地址或主机名。然后在客户机上运行网络对时系统中的客户端程序,在正确输入运行网络对时系统服务器端程序对应的主机名或者IP地址后,单击客户端程序中【获取】按钮,则在程序的文本框中显示服务器当前时间和日期和客户端当前的时间和日期。若发现二种存在差异,单击【对时】按钮,则将以服务器当前时间和日期来修正客户机的时间和日期。修改成功则弹出【修改成功】提示框,反之则弹出【不成功】提示框,图04是【UDP对时客户端】项目根据服务器端当前时间和日期信息成功更改本地时间和日期后的界面:



图04:【UDP对时客户端】项目的运行界面



七.总结:

本文详细介绍了UDP协议,.Net FrameWork SDK提供给Visual C#用以操作UDP协议的主要类库,以及通过一个具体而使用的示例——实现一个网络对时系统,介绍在Visual C#实现UDP协议的具体方法和过程。UDP由于其自身的缺点注定在某些领域无法利用它,但在可以利用它的领域,UDP以其快捷、简单、实用的特点正在受到更多程序员的欢迎。尤其在现代,网络运行态势越来越好的情况下,可以预见的是UDP在网络中的应用情景将更广阔。希望本文的内容对您掌握用Visual C#编写基于UDP的网络应用程序有所帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值