哲学中有句话:存在就是真理。每种语言能在某个时期出现,并能在各个产品线广泛应用,说明它具有一定的生命力。
如果选择不同的语言能解决我们的不同方案,难道不是我们所期望的理由?本文选择C#语言主要是使用C#开发WEB服务器,完成HTTP+XML数据通信。因项目对实时性没有苛刻性,换句话讲,如果实时性高也不会选择HTTP+XML。
本文的描述可以说是《IIOP.NET与CORBA中间件应用技术》的一种扩展画面。以C#编写的Web服务器和CORBA中间件服务器结合体。
1、 场景描述
计算机PC-2作为FEP服务器完成两项任务:
任务1:处理从ISCS(综合监控)主动上传的格式数据(HTTP+XML),并解析成对应的模拟量和数字量。其中ISCS为Web服务器的客户端,主动与PC-2实现连接,Web服务器的端口号:8080。连接方式为长连接。
任务2:把处理好的ISCS数据上传给PC-1,两者通讯方式以CORBA中间件实现。PC-2为中间件的服务端,PC-1为中间件的客户端。
任务1和任务2之间以共享队列的方式完成数据交换。任务2发现队列中有未取走的数据,会主动通知(接口onNotify)计算机PC-1。其通知机制也就是我们要讲的CORBA回调函数。
2、 回调函数
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础。
对于分布式组件代理体系CORBA,异步处理有多种方式,如回调接口、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务,他们主要负责消息的处理、派发、维护等工作。
回调接口的原理很简单,CORBA客户和服务器都具有双重角色,即充当服务器也是客户客户。对一些简单的异步处理过程,我们可以通过回调机制来实现。
3、 CORBA接口文件定义
// **********************************************************************
//
// Copyright (c) 2013
// 2013.06.27
// liuxuezong, PSD, Shanghai, China
// All Rights Reserved
//
// **********************************************************************
#ifndef _RTSIPWEBSERVER_IDL_
#define _RTSIPWEBSERVER_IDL_
//
// version 1.0.0
//
module rtsip
{
struct point_stream
{
string pointcode;
octet type;
float value;
};
typedef sequence <point_stream> PointStreamList;
// This exception is raised every time a failure or error is
// detected in a treatment.
exception GeneralException
{
string error_msg;
};
interface EventHandler
{
void onNotify(in long changes);
};
interface FepManager
{
PointStreamList getPointsValue() raises(GeneralException);
void setEventHandler(in EventHandler handler) raises(GeneralException);
};
};
#endif
//
// EOF rtsipwebserver.idl
//
4、C#实现部分
1)、FepManagerServer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using omg.org.CosNaming;
using Ch.Elca.Iiop;
using Ch.Elca.Iiop.Services;
using Ch.Elca.Iiop.Idl;
namespace FepWebService
{
class FepManagerServer
{
private static FepManagerImpl fepmanager = null;
public void run()
{
string nameServiceUrl ="corbaloc::localhost:10003/NameService";
// register the channel
int port = 10003;
IiopChannel chan =newIiopChannel(port);
ChannelServices.RegisterChannel(chan,false);
fepmanager = new FepManagerImpl();
string objectURI = "fepwebserver";
RemotingServices.Marshal(fepmanager, objectURI);
// publish the adder with an external name service
NamingContext nameService = (NamingContext)RemotingServices.Connect(
typeof(NamingContext), nameServiceUrl);
NameComponent[] name =newNameComponent[] {newNameComponent("fepwebserver") };
nameService.bind(name, fepmanager);
Console.WriteLine("Fep web server running");
Console.ReadLine();
// unpublish with external name service
nameService.unbind(name);
}
public bool isBindIiop()
{
return (fepmanager ==null ?false :true);
}
public void setQueue(PointQueue queue)
{
fepmanager.setQueue(queue);
}
public void onNotify(int changes)
{
fepmanager.onNotify(changes);
}
}
}
2)、FepManagerImpl.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using omg.org.CosNaming;
using Ch.Elca.Iiop;
using Ch.Elca.Iiop.Services;
using Ch.Elca.Iiop.Idl;
namespace FepWebService
{
///<summary>
/// the FepManagerImpl Implementation
///</summary>
public class FepManagerImpl : MarshalByRefObject, rtsip.FepManager, rtsip.EventHandler
{
private static rtsip.EventHandler handler_;
private static PointQueue queue_ = null;
public override object InitializeLifetimeService()
{
// live forever
return null;
}
public void setQueue(PointQueue queue)
{
queue_ = queue;
}
public rtsip.point_stream[] getPointsValue()
{
try
{
int nSize = queue_.GetCount();
nSize = (nSize > 1024 ? 1024 : nSize);
rtsip.point_stream[] points =new rtsip.point_stream[nSize];
for (int i = 0; i < nSize; i++)
{
rtsip.point_stream s =new rtsip.point_stream();
PointAttribute point = (PointAttribute)queue_.PopObj();
s.type = point.Type;
if (s.type ==PointAttribute.RTSIP_DATATYPE_DIGITAL)
{
s.value = Convert.ToByte(point.Value);
}
else
{
s.value = Convert.ToSingle(point.Value);
}
s.pointcode = point.PointCode;
points[i] = s;
}
return points;
}
catch (Exception e)
{
Console.WriteLine("exception: " + e);
}
return null;
}
public void setEventHandler(rtsip.EventHandler handler)
{
handler_ = handler;
}
public void onNotify(int changes)
{
try
{
if (handler_ != null && changes > 0)
{
handler_.onNotify(changes);
}
}
catch (Exception e)
{
Console.WriteLine("exception: " + e);
handler_ = null;
}
}
}
}
3)、PointQueue.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FepWebService
{
public struct PointAttribute
{
public const byte RTSIP_DATATYPE_NULL = 0;
public const byte RTSIP_DATATYPE_ANALOG = 1;
public const byte RTSIP_DATATYPE_DIGITAL = 2;
public byte Type;
public string PointCode;
public string Value;
public PointAttribute(byte type,string pointcode,string value)
{
this.Type = type;
this.PointCode = pointcode;
this.Value = value;
}
}
public class PointQueue
{
#region Constants
public const string ObjectType = "FepWebService.PointAttribute";
#endregion Constants
private static RtsipQueue queue = new RtsipQueue();
public void PushObj(Object obj)
{
queue.enqueue(obj);
}
public Object PopObj()
{
return queue.dequeue();
}
public int GetCount()
{
return queue.getSize();
}
}
}
4)、RtsipQueue.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FepWebService
{
public class RtsipQueue
{
private static int QUEUE_MAX_LEN = 4096;
private int m_nRead;
private static int m_nWrite;
private static Object[] data;
public RtsipQueue()
{
data = new Object[QUEUE_MAX_LEN];
m_nRead = 0;
m_nWrite = 0;
}
public int getSize()
{
int num = (m_nWrite - m_nRead + QUEUE_MAX_LEN) % QUEUE_MAX_LEN;
return num;
}
public Object dequeue()
{
if (m_nWrite == m_nRead)
{
return null;
}
Object obj = data[m_nRead];
m_nRead = (m_nRead + 1) % QUEUE_MAX_LEN;
return obj;
}
public void enqueue(Object obj)
{
int nWritep = m_nWrite;
Object pData = data[nWritep];
pData = null;
data[nWritep] = obj;
nWritep = (nWritep + 1) % QUEUE_MAX_LEN;
m_nWrite = nWritep;
}
}
}
5)、Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using rtsip;
using FepWebService;
using HybridDSP.Net.HTTP;
using System.Xml;
using System.IO;
using System.Threading;
namespace FepWebService
{
class RtsipHandler : IHTTPRequestHandler
{
public static PointQueue queue = new PointQueue();
public static FepManagerServer manager_ = null;
public RtsipHandler(FepManagerServer manager)
{
manager_ = manager;
}
public void ParseXML(string PointCode, string strXML)
{
XmlDocument XDoc =newXmlDocument();
XDoc.LoadXml(strXML);
XmlNodeList nodelist = XDoc.SelectNodes("/vjournal/description");
foreach (XmlNode nodein nodelist)
{
byte type = PointAttribute.RTSIP_DATATYPE_NULL;
string strValue = node.InnerText;
string strTemp = PointCode;
strTemp.ToLower();
if (strTemp.IndexOf("ai") > 0)
{
type = PointAttribute.RTSIP_DATATYPE_ANALOG;
}
else if (strTemp.IndexOf("di") > 0)
{
type = PointAttribute.RTSIP_DATATYPE_DIGITAL;
}
else
{
type = PointAttribute.RTSIP_DATATYPE_DIGITAL;
}
PointAttribute point =newPointAttribute(type, PointCode, strValue);
queue.PushObj(point);
}
}
public void HandleRequest(HTTPServerRequest request,HTTPServerResponse response)
{
Stream body = request.GetRequestStream();
long len = request.GetContentLength();
if (len > 0)
{
byte[] b = new byte[len];
body.Read(b, 0, Convert.ToInt32(len));
string strBody = Encoding.ASCII.GetString(b);
string strLocation = request.Get("content-location");
Console.WriteLine("content-location:{0}", strLocation);
int nIndex = strLocation.LastIndexOf("/") + 1;
int nSize = strLocation.Length - nIndex;
string PointCode = strLocation.Substring(nIndex, nSize);
Console.WriteLine("Body:{0}", strBody);
ParseXML(PointCode, strBody);
// "HTTP/1.1 200 OK\r\nContent-Length:0\r\n\r\n"
response.StatusAndReason = HTTPServerResponse.HTTPStatus.HTTP_OK;
response.ContentLength = 0;
response.Send();
}
}
public static void ProcessXmlData()
{
while (true)
{
if (manager_ != null && manager_.isBindIiop())
{
manager_.setQueue(queue);
}
int nCount = queue.GetCount();
if (manager_ != null && nCount > 0)
{
manager_.onNotify(nCount);
}
Thread.Sleep(50);
}
}
}
class RequestHandlerFactory : IHTTPRequestHandlerFactory
{
private static Thread _thread;
private static FepManagerServer manager_ = null;
public RequestHandlerFactory(FepManagerServer manager)
{
manager_ = manager;
_thread = new Thread(RtsipHandler.ProcessXmlData);
_thread.Start();
}
public IHTTPRequestHandler CreateRequestHandler(HTTPServerRequest request)
{
RtsipHandler rtsipHandle =newRtsipHandler(manager_);
return rtsipHandle;
}
}
class Program
{
static void Main(string[] args)
{
FepManagerServer manager =newFepManagerServer();
RequestHandlerFactory factory =newRequestHandlerFactory(manager);
HTTPServer server =newHTTPServer(factory, 8080);
server.Start();
manager.run();
server.Stop();
}
}
}
5、C++实现部分
省略
6、结束语
本文讲述了回调函数在CORBA中的使用情况,以C#工程典型的案例分析回调过程。远程函数调用结合回调函数功能,基本满足一般任务要求。