湖南理工学院毕业设计(论文)
第3章 监控软件程序实现
3.1 软件设计方案
监控软件设计的核心在于服务器和客户端之间的实时通信。本文提供了两种通信协议,分别是面向连接的TCP/IP协议和无连接的UDP协议,然而在工业控制中必须保证数据传输的可靠性,因此我们选择了安全性高的TCP/IP协议作为通讯协议。此外本文还提供了同步通信和异步异步通信两种模式可供选择。同步通信属于串行通信,用次模式,要求客户端在发送一条请求后,必须等到服务器做出相应回应,才能够发送下一条请求。异步通信属于并行通信,此模式下客户端发送一条请求之后,不必等到服务器做出相应回应就可发送下一条请求。在工业控制中,往往需要保证数据传输的实时性,同步通信模式显然不符合要求,因此我们选择能实时传输的异步通信模式。
3.2 服务器设计
3.2.1 界面设计
为了方便演示与测试,创建Windows窗体应用程序,进行软件设计。而要生成ActiveX控件则需要创建Windows控件库项目,但两者界面设计的方法一致(注:该服务器界面以及下章的客户端界面仅为测试通信使用,作为Active控件发布的界面数据无需手动输入,数据采集完成后直接存储到内部Buffer进行发送。而正式的工业液位监控界面由组态王设计,本文不介绍)。创建好项目后,打开工具箱,拖出我们所需的工具控件如:Lable、Button、TextBox等进行界面设计,之后修改其对应的属性,双击后则可进行其他部分的代码编写。最终界面如图3.1所示。
图3.1 server界面设计
7
湖南理工学院毕业设计(论文)
3.2.2 Socket编程
Microsoft.Net Framework 为程序访问Internet实现了可分层、可扩展的网络服务,其命名空间System.Net和System.Net.Sockets包含大量的类能够便于网络通讯程序的开发。所以在编写前应该添加using System.Net、using System.Net.Sockets和using System.Threading三个命名空间。具体编程可分为以下步骤: (1)建立服务器连接
首先要创建Socket对象,使用Socket类的构造方法实现: Socket listener = new
Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
参数如下:AddressFamily:Socket使用的寻址方案;SocketType:Socket类型;ProtocolType:Socket使用的协议,这里所用的是TCP/IP协议。
当创建Socket后,服务器端则要通过Bind()方法绑定所指定的端口,使Socket和一个本地终端相联。这里在服务器端可设置两种方式进行连接,一种是DNS创建域名接连的方式:
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName()); 另一种是IP地址的连接方式:
IPAddress ipAddress = ipHostInfo.AddressList[0]; 这里是系统自动获取本机IP,也可以手动输入IP地址方法如下:
IPAddress ipAddress = IPAddress.Parse(\
这里采取IP地址连接的方式,设置好IP地址和端口号后使用Bind()方法进行绑定:
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000); listener.Bind(localEndPoint);
然后通过Listen方法监听该端口上的连接申请,当监听到端口的连接申请后。同步模式时,服务器调用Accept方法允许连接申请。异步模式时,服务器能够调用BeginAccept方法和EndAccept方法实现与客户端的通信。BeginAccept在异步模式下尝试连接,它准许其余进程直接运行,而不必等候连接建立。在使用BeginAccept方法之前,必需调用Listen方法来监听连接申请,BeginAccept的函数原型为:
BeginAccept(AsyncCallback AsyncCallback, Ojbect state);
这里AsyncCallBack:代表回调函数;state:代表状态信息,必须确保state中包含socket的句;调用BeginAccept方法的根本流程是:1、建立本机终节点,
8
湖南理工学院毕业设计(论文)
并建立新的socket与本机终节点进行绑定;2、在端口上监听是否有新的连接申请;3、申请开始接入新的连接,将其传入Socket的实例或者StateOjbect的实例。调用BeginAccept()方法完成后,当有新的连接产生,就会使用回调函数,此回调函数一定得包含用于终结接入连接操作的EndAccept()方法,其原型为:
Socket EndAccept(IAsyncResult iar); 那么服务器构建代码如下:
public static void StartListening() {
IPAddress local = IPAddress.Parse(\IPEndPoint iep = new IPEndPoint(local,13000); Socket listener = new
Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp); server.Bind(iep); server.Listen(20);
server.BeginAccecpt(new AsyncCallback(Accept), listener); }
void Accept(IAsyncResult iar) {
Socket MyServer = (Socket)iar.AsyncState; Socket service = MyServer.EndAccept(iar); }
(2)发送与接收数据
在创建了套接字的连接后,就能够使服务器端和客户端之间进行数据通信了。异步通信用BeginSend和EndSend方法来完成数据发送。在使用BeginSend方法前要确认双方都已成功连接,不然会出错误。BeginSend方法原型为:
Socket.BeginSend(Byte[],Int32, Int32, SocketFlags, AsyncCallback, Object);
参数如下:buffer:Byte类型的数组,包含将发送的数据;offset:buffer参数中发送数据的起始位置,该位置从零开始计数;size:将发送的字节数;socketFlags:SocketFlags值的按位组合;callback:AsyncCallback委托;state:一个对象,包含此申请的状态消息;返回值:调用异步通讯发送IAsyncResult。创建一个完成 AsyncCallback 的回调方法并将名字传入
9
湖南理工学院毕业设计(论文)
BeginSend 方法。state 参数一定得包含用来通讯的连接。如果回调要求更多信息,那么可构建一个小型的类用于存储Socket和其他必须的信息。经过state 参数将此类的一个实例传递给 BeginSend 方法。回调方法应使用EndSend方法。当应用程序使用 BeginSend方法时,系统将运用一个单独的线程完成特定的回调方法,并阻止 EndSend,直到Socket发送了申请的字节数或引发了异常。相关代码如下:
private static void Send(Socket handler, String data) {
byte[] byteData = Encoding.ASCII.GetBytes(data); handler.BeginSend(byteData,0,byteData.Length,0,new AsyncCallback(SendCallback), handler); }
private static void SendCallback(IAsyncResult ar) { try {
Socket handler = (Socket)ar.AsyncState; int bytesSent = handler.EndSend(ar); handler.Shutdown(SocketShutdown.Both); handler.Close(); }
catch (Exception e) { } }
异步通信用BeginReceive和EndReceive方法来接收数据,其BeginReceive方法原型为:
Socket.BeginReceive(Byte[],Int32,Int32,SocketFlags,AsyncCallback,Object)
参数如下:buffer:Byte类型的数组,它是保存接收到的数据的位置;offset:buffer参数中保存所接收数据的位置,该位置从零开始计数;size:要接收的字节数;socketFlags:SocketFlags值的按位组合;callback:一个AsyncCallback委托,它援用操作完成时要应用的方法;state:一个用户定义对象,其中宝库接收操作的相干信息。操作完成时,此对象会传软EndReceive方法;返回值:使用异步方法读取IAsyncResult。异步BeginReceive操作应使
10
湖南理工学院毕业设计(论文)
用EndReceive方法完成。一般该方法由callback 实现。在操作完成前方法不会进入阻塞状态。如需一直阻塞到操作完成时则应使用Receive方法进行重载。相关代码如下:
private static void Receive(Socket client) { try {
StateObject state = new StateObject(); state.workSocket = client;
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e) { } }
private static void ReceiveCallback(IAsyncResult ar) { try {
StateObject state = (StateObject)ar.AsyncState; Socket client = state.workSocket; int bytesRead = client.EndReceive(ar); if (bytesRead > 0) {
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead);
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,new AsyncCallback(ReceiveCallback), state);
} else {
11