NET Remoting 双向通信(转)
最近有个项目,大致需求是,服务端发送消息通知客户端上传指定的数据,然后处理后一部分显示在服务端界面上。也是在网上胡乱搜索一片,看到一篇Remoting广播事件的博客+Remoting觉得这么还可以做。
大致原理是:通过服务端广播事件,客户端通过调用远程类将数据以参数的方式传给服务端,然后激活服务端界面层的事件就达到双向了。都是靠远程类里的2个事件,一个给服务端,一个给客户端,分别交叉执行。这就相当于: 服务端界面--远程类--客户端界面,远程类起到了一个中间人的作用样,是吧?
先看看服务端封装的Remoting的类
{
HttpChannel tcpC;
public static RemotingObject.Remoter Obj = null;
static RemotingServer instance = new RemotingServer();
public static RemotingServer GetRemotingServer
{
get { return instance; }
}
RemotingServer()
{
//tcpC = new TcpChannel(9000);
}
public void ServerTakeOn()
{
//if (!ExistServer(tcpC.ChannelName))
//{
RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject.Remoter), "RemotingService", WellKnownObjectMode.Singleton);
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 9500;
tcpC = new HttpChannel(props, clientProvider, serverProvider);
ChannelServices.RegisterChannel(tcpC);
Obj = new RemotingObject.Remoter();
ObjRef objRef = RemotingServices.Marshal(Obj, "RemotingMessage.soap");
//}
}
public void ServerTakeOff()
{
if (ExistServer(tcpC.ChannelName))
{
tcpC.StopListening(null);
ChannelServices.UnregisterChannel(tcpC);
}
}
private bool ExistServer(string serverName)
{
bool tmp_b = false;
IChannel[] list = ChannelServices.RegisteredChannels;
foreach (IChannel ic in list)
{
HttpChannel tmp_tcp = (HttpChannel)ic;
if(tmp_tcp.ChannelName==serverName)
{
tmp_b = true;
break;
}
}
return tmp_b;
}
}
下面2句代码特别的重要是服务端界面能和客户端共同操作的远程类
ObjRef objRef = RemotingServices.Marshal(Obj, "RemotingMessage.soap");
接下来看看客户端的Remoting是怎么样的
{
HttpChannel tcp;
IMessage imessage = null;
EventWrapper wrapper = null;
string _mes;
public string ErrorMes
{
get { return _mes; }
}
public IMessage GetObject1
{
get { return imessage; }
}
public EventWrapper GetObject2
{
get { return wrapper; }
}
static RemotingClient _client = new RemotingClient();
public static RemotingClient GetObject
{
get { return _client; }
}
RemotingClient()
{
}
public bool Connect(string serverIP)
{
try
{
//_object = (RemotingObject.Remoter)Activator.GetObject(typeof(RemotingObject.Remoter),
// string.Format("tcp://{0}:9000/RemotingService", serverIP));
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 0;
tcp = new HttpChannel(props, clientProvider, serverProvider);
ChannelServices.RegisterChannel(tcp);
//RemotingConfiguration.RegisterActivatedClientType(typeof(IMessage), string.Format("tcp://{0}:9000/RemotingService", serverIP));
imessage = (IMessage)Activator.GetObject(typeof(IMessage), string.Format("http://{0}:9500/RemotingMessage.soap", serverIP));
wrapper = new EventWrapper();
return true;
}
catch(Exception ex)
{
_mes = ex.Message;
return false;
}
}
public void UnConnect()
{
if (ExistServer(tcp.ChannelName))
{
ChannelServices.UnregisterChannel(tcp);
}
}
private bool ExistServer(string serverName)
{
bool tmp_b = false;
IChannel[] list = ChannelServices.RegisteredChannels;
foreach (IChannel ic in list)
{
HttpChannel tmp_tcp = (HttpChannel)ic;
if (tmp_tcp.ChannelName == serverName)
{
tmp_b = true;
break;
}
}
return tmp_b;
}
}
是通过HTTP协议的,如果是域名的话,好像要先解析成IP吧?连接服务端,获取远程对象,给出属性返回让客户端界面能操作远程类
接下来就是客户端连接的代码,连接成功后,取出远程类,关联远程类的事件
private void btnConnect_Click(object sender, EventArgs e)
{
if (!client.Connect(tbxIp.Text))
{
MesBox.Show(client.ErrorMes, 0);
return;
}
imessage = client.GetObject1;
wrapper = client.GetObject2;
try
{
wrapper.SendMessageEven += new SendMessageEvenHandler(GetTxt);
imessage.SendMessageEven += new SendMessageEvenHandler(wrapper.SendMessageing);
}
catch (Exception ex)
{
MesBox.Show(ex.Message, 0);
return;
}
btnConnect.Text = "已连接";
btnConnect.Enabled = false;
}
#endregion
private void GetTxt(string txt)
{
if (txt == "A")
{
string ip = GetlocalIP();
List<string> tmp = GetHardInfo();
imessage.AddInfo(ip, tmp);
}
}
#endregion
上面代码中参数TXT==A,那是我自己胡弄的一个标识,不重要,然后获取客户端的IP和硬盘信息,通过调用AddInfo方法传给服务端原程类。
在远程类中有事件处理的方法,在AddInfo方法内会调用,那么关联服务端界面的方法就会被执行,数据也通过参数传递过去了
{
#region IMessage 成员
public event SendMessageEvenHandler SendMessageEven;
public List<string> HardDeskInfo = new List<string>();
public void SendMessage( string txt)
{
if (SendMessageEven != null)
{
SendMessageEvenHandler tmp_even = null;
foreach (Delegate dl in SendMessageEven.GetInvocationList())
{
try
{
tmp_even = (SendMessageEvenHandler)dl;
tmp_even(txt);
}
catch
{
SendMessageEven -= tmp_even;
}
}
}
}
public void AddInfo(string IP, List<string> hardinfo)
{
string tmp = string.Empty;
foreach (string s in hardinfo)
{
//获取剩余大小
string tmp1 = s.Substring(s.LastIndexOf("@") + 1);
//获取分区@总大小
string tmp2 = s.Substring(0, s.Length - tmp1.Length - 1);
//获取总大小
string tmp3 = tmp2.Substring(tmp2.LastIndexOf("@") + 1);
//获取分区
string tmp4 = tmp2.Substring(0, tmp2.Length - tmp3.Length - 1);
//格式是 IP@分区@总大小@剩余大小
tmp = string.Format("{0}@{1}@{2}@{3}", IP, tmp4, tmp3, tmp1);
HardDeskInfo.Add(tmp);
ClientReciveData(tmp);
}
}
public void ClearInfo()
{
HardDeskInfo.Clear();
}
public override object InitializeLifetimeService()
{
return null;
}
#endregion
#region 激活服务端事件
public delegate void ClientReciveDataEvenHandler(string txt);
public event ClientReciveDataEvenHandler ClientReciveDataEven;
public void ClientReciveData(string txt)
{
if (ClientReciveDataEven != null)
{
ClientReciveDataEvenHandler tmp_even = null;
foreach (Delegate dl in ClientReciveDataEven.GetInvocationList())
{
try
{
tmp_even = (ClientReciveDataEvenHandler)dl;
tmp_even(txt);
}
catch
{
ClientReciveDataEven -= tmp_even;
}
}
}
}
#endregion
}
上面这个就是原创类了,是继承于接口IMessage
{
public delegate void SendMessageEvenHandler(string txtt);
public interface IMessage
{
event SendMessageEvenHandler SendMessageEven;
void SendMessage(string txt);
void AddInfo(string IP, List<string> hardinfo);
}
}
接口和远程类是不同的类库生成的DLL,在服务端2个都会被调用,但是客户端只会调用接口的DLL,中间还有一个事件适配器吧?是这么叫的吧?这个方式源于Remoting服务端事件广播
{
public class EventWrapper : MarshalByRefObject
{
public event SendMessageEvenHandler SendMessageEven;
public void SendMessageing( string txt)
{
SendMessageEven(txt);
}
public override object InitializeLifetimeService()
{
return null;
}
}
}
这2个的关系我到现在还没搞懂的,不过能使用它们就很不错啦
最后看看服务端界面方法关联的远程类事件
private void frmMain_Load(object sender, EventArgs e)
{
_server = RemotingServer.GetRemotingServer;
_server.ServerTakeOn();
RemotingServer.Obj.ClientReciveDataEven += new RemotingObject.Remoter.ClientReciveDataEvenHandler(ClientReciveData);
}
#endregion
#region 窗体关闭
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
RemotingServer.Obj.ClientReciveDataEven -= new RemotingObject.Remoter.ClientReciveDataEvenHandler(ClientReciveData);
_server.ServerTakeOff();
}
#endregion
#region 远程类事件
private void ClientReciveData(string txt)
{
SetLst(txt);
}
#endregion
#region 设置列表
private void SetLst(string tmp)
{
string ip = string.Empty;
string dir = string.Empty;
string all = string.Empty;
string free = string.Empty;
//剩余大小
string tmp1 = tmp.Substring(tmp.LastIndexOf("@") + 1);
//IP@分区@总大小
string tmp2 = tmp.Substring(0, tmp.Length - tmp1.Length - 1);
//总大小
string tmp3 = tmp2.Substring(tmp2.LastIndexOf("@") + 1);
//IP@分区
string tmp4 = tmp2.Substring(0, tmp2.Length - tmp3.Length - 1);
//分区
string tmp5 = tmp4.Substring(tmp4.LastIndexOf("@") + 1);
//IP
string tmp6 = tmp4.Substring(0, tmp4.Length - tmp5.Length - 1);
ip = tmp6;
dir = tmp5;
all = tmp3;
free = tmp1;
ListViewItem itm = new ListViewItem();
itm.SubItems[0].Text = ip;
itm.SubItems.Add(dir);
itm.SubItems.Add(all);
itm.SubItems.Add(free);
lst.Items.Add(itm);
}
接受到参数传递的数据,处理下放到ListView显示出来,不过我记得Remoting远程类所激活的事件都应该是子线程的吧?怎么我这没报错(子线程不能操作主线程的控件)呢?