二、利用事件实现客户端向服务器通信
接着上一篇,按照我的思路,远程对象中定义的事件在客户端触发,而在服务器端订阅,应该可以成功。现在放弃之前的示例代码,自己重新写一个版本,代码如下:
远程对象:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Remoting { public class RemotingObject:MarshalByRefObject { public delegate void MyDelegate(string msg); public static event MyDelegate myEvent; //触发事件 public void TriggerEvent(string msg) { if (myEvent != null) myEvent(msg); } //无限生命周期 public override object InitializeLifetimeService() { return null; } } }
服务器:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using Remoting; namespace Server { public partial class ServerForm : Form { public ServerForm() { InitializeComponent(); StartServer(); } //开启服务器 public void StartServer() { TcpChannel tcpchannel = new TcpChannel(8080); ChannelServices.RegisterChannel(tcpchannel,false); RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject),"url",WellKnownObjectMode.Singleton); RemotingObject.myEvent += new RemotingObject.MyDelegate(RemotingObject_myEvent); } void RemotingObject_myEvent(string msg) { //跨线程调用 textBox2.Invoke(new Action<string>(str => { textBox2.Text = str; }), msg); } } }
客户端:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using Remoting; namespace Client { public partial class ClientForm : Form { RemotingObject obj; public ClientForm() { InitializeComponent(); StartClient(); } private void 发送_Click(object sender, EventArgs e) { obj.TriggerEvent("客户端--" + this.ClientIP() + System.Environment.NewLine + textBox1.Text + System.Environment.NewLine); } //开启客户端 public void StartClient() { TcpChannel tcpchannel = new TcpChannel(0); ChannelServices.RegisterChannel(tcpchannel, false); obj = (RemotingObject)Activator.GetObject(typeof(RemotingObject), "tcp://localhost:8080/url"); } //获取本地ip public string ClientIP() { return System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName())[0].ToString(); } } }
运行效果如下:
哈哈,如我所愿,利用事件确实实现了客户端与服务器的通信。
可是,这里面有一个小问题:在服务器订阅事件的时候我用到
RemotingObject.myEvent += new RemotingObject.MyDelegate(RemotingObject_myEvent);
这一句其实是我不得已而为之的,为什么这么说的。因为这里myEvent是静态的,只有这样我才可以用RemotingObject.myEvent订阅到事件。但是如果myEvent不是静态的呢?这时候如何去实例化一个对象,并且用"对象.myEvent"来订阅事件呢?如果我在服务器端自己new一个对象,那这个对象肯定和客户端激活得到的对象不是同一个引用。
(注:上面的Demo在此处下载http://download.csdn.net/detail/kkkkkxiaofei/5645629)
带着这个疑问,我找了些资料。有了重大发现。
[SecuritySafeCritical]
public static ObjRef Marshal(MarshalByRefObject Obj, string URI);
来看看官方的解释把:将给定的 System.MarshalByRefObject 转换为具有指定 URI 的 System.Runtime.Remoting.ObjRef 类的实例。
看到实例两个字我就激动了, 这里我的RemotingObject类就继承了System.MarshalByRefObject。这个ObjRef 类是做什么的?如果将其转换为ObjRef 类的实例又能怎么样呢?
再来看看ObjRef 吧。
ObjRef :存储生成代理以与远程对象通信所需要的所有信息。
有了上面的概念,我又有了点思路了:
之前客户端(RemotingObject)Activator.GetObject(typeof(RemotingObject), "tcp://localhost:8080/url")得到的这个对象,是服务器注册的RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject),"url",WellKnownObjectMode.Singleton)这个对象的引用。在服务器端并不能获取这个对象。那如果我在服务器端显示创建一个对象,然后让客户端刚好能获取这个引用不就行了么。Marshal方法不就是显示生成服务器端的RemotingObject对象的代理么,客户端激活的代码已然不变,利用ip地址和url就能找到这个对象,这样2个程序域中的2个对象其实就是同一个引用。这么一来,就可以解决掉事件只能是静态的尴尬局面了,好了是时候验证我的想法了。代码如下,
远程对象:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Remoting { public class RemotingObject:MarshalByRefObject { public delegate void MyDelegate(string msg); public event MyDelegate myEvent; //触发事件 public void TriggerEvent(string msg) { if (myEvent != null) myEvent(msg); } //无限生命周期 public override object InitializeLifetimeService() { return null; } } }
客户端:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using Remoting; namespace Client { public partial class ClientForm : Form { RemotingObject obj; public ClientForm() { InitializeComponent(); StartClient(); } private void 发送_Click(object sender, EventArgs e) { obj.TriggerEvent("客户端--" + this.ClientIP() + System.Environment.NewLine + textBox1.Text + System.Environment.NewLine); } //开启客户端 public void StartClient() { TcpChannel tcpchannel = new TcpChannel(0); ChannelServices.RegisterChannel(tcpchannel, false); obj = (RemotingObject)Activator.GetObject(typeof(RemotingObject), "tcp://localhost:8080/url"); } //获取本地ip public string ClientIP() { return System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName())[0].ToString(); } } }
服务器:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using Remoting; namespace Server { public partial class ServerForm : Form { RemotingObject marshal_obj; public ServerForm() { InitializeComponent(); StartServer(); } //开启服务器 public void StartServer() { TcpChannel tcpchannel = new TcpChannel(8080); ChannelServices.RegisterChannel(tcpchannel,false); //RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject),"url",WellKnownObjectMode.Singleton); marshal_obj = new RemotingObject(); ObjRef objRef = RemotingServices.Marshal(marshal_obj, "url"); marshal_obj.myEvent += new RemotingObject.MyDelegate(RemotingObject_myEvent); } void RemotingObject_myEvent(string msg) { //跨线程调用 textBox2.Invoke(new Action<string>(str => { textBox2.Text = str; }), msg); } } }
运行结果很乐观,如期所料
看来用Marshal方法确实可以让服务器和客户端都有一份远程对象的引用,咦,那这么说,服务器岂不是也可以主动触发事件,然后让客户端来绑定,这样就能实现服务器向客户端通信。哈哈,离我的项目需求又近了一步,不错不错,下篇试试实现这个问题。
(注:上面的Demo在此处下载http://download.csdn.net/detail/kkkkkxiaofei/5646103)