闲来无事想玩玩双向通讯,完成相似QQ的互发音讯的功用。于是乎最先进修.Net Remoting.
.Net Remoting 是由客户端经由过程Remoting,接见通道以取得效劳端对象,再经由过程代办剖析为客户端对象来完成通讯的。也就是说对象是由效劳端建立的。
先上代码
起首是ICommand库
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ICommand { public interface IRemotingObject { event SendHandler ClientToServer; event ReceiveHandler ServerToClient; event UserChangedHandler Login; event UserChangedHandler Exit; /// <summary> /// 加法运算 /// </summary> /// <param name="x1">参数1</param> /// <param name="x2">参数2</param> /// <returns></returns> string SUM(int x1, int x2); /// <summary> /// 猎取效劳端事宜列表 /// </summary> Delegate[] GetServerEventList(); /// <summary> /// 发送音讯 /// </summary> /// <param name="info"></param> /// <param name="toName"></param> void ToServer(object info, string toName); /// <summary> /// 吸收信息 /// </summary> /// <param name="info"></param> /// <param name="toName"></param> void ToClient(object info, string toName); void ToLogin(string name); void ToExit(string name); } /// <summary> /// 客户端发送音讯 /// </summary> /// <param name="info">信息</param> /// <param name="toName">发送给谁,""示意一切人,null示意没有吸收效劳器本身吸收,其他示意指定或人</param> public delegate void SendHandler(object info, string toName); /// <summary> /// 客户端吸收音讯 /// </summary> /// <param name="info">信息</param> /// <param name="toName">发送给谁,""示意一切人,null示意没有吸收效劳器本身吸收,其他示意指定或人</param> public delegate void ReceiveHandler(object info, string toName); /// <summary> /// 用户信息事宜 /// </summary> /// <param name="name">用户名</param> public delegate void UserChangedHandler(string name); }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ICommand { public class SwapObject : MarshalByRefObject { public event ReceiveHandler SwapServerToClient { add { _receive += value; } remove { _receive -= value; } } /// <summary> /// 吸收信息 /// </summary> /// <param name="info"></param> /// <param name="toName"></param> public void ToClient(object info, string toName) { if (_receive != null) _receive(info, toName); } //无穷生命周期 public override object InitializeLifetimeService() { return null; } private ReceiveHandler _receive; } }
第一个类就是定义一些接口,和一些托付,没有实质性的东西。
第二个类是定义了上一个接口类中的ToClient的事宜和要领,作用以后会讲到。
然后就是集成ICommand接口的实质性的数据类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ICommand; namespace NetRemoting { public class RemotingObject : MarshalByRefObject, IRemotingObject { /// <summary> /// 发送事宜 /// </summary> public event SendHandler ClientToServer { add { _send += value; } remove { _send -= value; } } /// <summary> /// 吸收音讯事宜 /// </summary> public event ReceiveHandler ServerToClient; /// <summary> /// 发送事宜 /// </summary> public event UserChangedHandler Login { add { _login += value; } remove { _login -= value; } } /// <summary> /// 发送事宜 /// </summary> public event UserChangedHandler Exit { add { _exit += value; } remove { _exit -= value; } } /// <summary> /// 加法运算 /// </summary> /// <param name="x1">参数1</param> /// <param name="x2">参数2</param> /// <returns></returns> public string SUM(int x1, int x2) { return x1 + "+" + x2 + "=" + (x1 + x2); } /// <summary> /// 绑定效劳端向客户端发送音讯的事宜要领 /// </summary> /// <param name="receive">吸收事宜</param> public Delegate[] GetServerEventList() { return this.ServerToClient.GetInvocationList(); } /// <summary> /// 发送音讯 /// </summary> /// <param name="info"></param> /// <param name="toName"></param> public void ToServer(object info, string toName) { if (_send != null) _send(info, toName); } /// <summary> /// 吸收音讯 /// </summary> /// <param name="info"></param> /// <param name="toName"></param> public void ToClient(object info, string toName) { if (_receive != null) _receive(info, toName); } /// <summary> /// 登录 /// </summary> /// <param name="name">用户名</param> public void ToLogin(string name) { if (!_nameHash.Contains(name)) { _nameHash.Add(name); if (_login != null) _login(name); } else { throw new Exception("用户已存在"); } } /// <summary> /// 退出 /// </summary> /// <param name="name">用户名</param> public void ToExit(string name) { if (_nameHash.Contains(name)) { _nameHash.Remove(name); if (_exit != null) _exit(name); } } private SendHandler _send; private ReceiveHandler _receive; private UserChangedHandler _login; private UserChangedHandler _exit; private HashSet<string> _nameHash = new HashSet<string>(); } }
该类集成了MarshalByRefObject
由于Remoting通报的对象是以援用的体式格局,因而所通报的长途对象类必需继续MarshalByRefObject。MSDN对MarshalByRefObject的申明是:MarshalByRefObject 是那些经由过程运用代办交流音讯来逾越应用程序域边境举行通讯的对象的基类。不是从 MarshalByRefObject 继续的对象会以隐式体式格局按值封送。当长途应用程序援用一个按值封送的对象时,将逾越长途处置惩罚边境通报该对象的副本。由于您愿望运用代办要领而不是副本要领举行通讯,因而须要继续MarshallByRefObject。
该类重如果定义了一些要领用于客户端触发事宜,ToServer,ToClient,ToLogin,ToExit以及一些事宜,客户端发向效劳端的事宜,和效劳端发向客户端的事宜。
_nameHash 只是纪录有哪些用户登录了。
接下去就是客户端和效劳端了。
起首效劳端:
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.Http; using NetRemoting; using System.Collections; using System.Runtime.Serialization.Formatters; using ICommand; namespace NetRemotingServer { public partial class Server : Form { public Server() { InitializeComponent(); Initialize(); } /// <summary> /// 注册通道 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Server_Load(object sender, EventArgs e) { ChannelServices.RegisterChannel(_channel, false); //RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), "SumMessage", WellKnownObjectMode.Singleton); //a计划 /*将给定的 System.MarshalByRefObject 转换为具有指定 URI 的 System.Runtime.Remoting.ObjRef 类的实例。 ObjRef :存储生成代办以与长途对象通讯所须要的一切信息。*/ ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage");//b计划 _remotingObject.ClientToServer += (info, toName) => { rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(info.ToString() + "\r\n"); })); SendToClient(info, toName); }; _remotingObject.Login += (name) => { rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(name + " 登录" + "\r\n"); })); }; _remotingObject.Exit += (name) => { rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(name + " 退出" + "\r\n"); })); }; } /// <summary> /// 注销通道 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Server_FormClosing(object sender, FormClosingEventArgs e) { if (_channel != null) { _channel.StopListening(null); ChannelServices.UnregisterChannel(_channel); } } /// <summary> /// 播送音讯 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) { SendToClient(txtSend.Text, txtName.Text); } /// <summary> /// 发送音讯到客户端 /// </summary> /// <param name="info"></param> /// <param name="toName"></param> private void SendToClient(object info, string toName) { //foreach (var v in _remotingObject.GetServerEventList()) //{ // try // { // ReceiveHandler receive = (ReceiveHandler)v; // receive.BeginInvoke(info, toName, null, null); // } // catch // { } // } _remotingObject.ToClient(txtSend.Text, txtName.Text); } /// <summary> /// 初始化 /// </summary> private void Initialize() { //设置反序列化级别 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full;//支撑一切范例的反序列化,级别很高 IDictionary idic = new Dictionary<string, string>(); idic["name"] = "serverHttp"; idic["port"] = "8022"; _channel = new HttpChannel(idic, clientProvider, serverProvider); _remotingObject = new RemotingObject(); } HttpChannel _channel; private RemotingObject _remotingObject; } }
然后客户端:
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.Http; using ICommand; using System.Runtime.Serialization.Formatters; using System.Collections; namespace NetRemotingClient { public partial class Client : Form { public Client() { InitializeComponent(); } /// <summary> /// 注册通道 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Client_Load(object sender, EventArgs e) { try { //设置反序列化级别 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full;//支撑一切范例的反序列化,级别很高 //信道端口 IDictionary idic = new Dictionary<string, string>(); idic["name"] = "clientHttp"; idic["port"] = "0"; HttpChannel channel = new HttpChannel(idic, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel, false); _remotingObject = (IRemotingObject)Activator.GetObject(typeof(IRemotingObject), "http://localhost:8022/SumMessage"); //_remotingObject.ServerToClient += (info, toName) => { rtxMessage.AppendText(info + "\r\n"); }; SwapObject swap = new SwapObject(); _remotingObject.ServerToClient += swap.ToClient; swap.SwapServerToClient += (info, toName) => { rtxMessage.Invoke((MethodInvoker)(() => { if (toName == txtLogin.Text || toName == "") rtxMessage.AppendText(info + "\r\n"); })); }; } catch (Exception ex) { MessageBox.Show(ex.Message); } } /// <summary> /// 登录 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnLogin_Click(object sender, EventArgs e) { try { if (txtLogin.Text == "") throw new Exception("用户名不得为空"); _remotingObject.ToLogin(txtLogin.Text); } catch (Exception ex) { MessageBox.Show(ex.Message); } } /// <summary> /// 退出 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Client_FormClosing(object sender, FormClosingEventArgs e) { try { _remotingObject.ToExit(txtLogin.Text); } catch { } } /// <summary> /// 发送 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) { //rtxMessage.AppendText(_remotingObject.SUM(2, 4) + "\r\n"); _remotingObject.ToServer(txtSend.Text, txtName.Text); } private IRemotingObject _remotingObject; } }
效劳端完成步骤:
1、注册通道
要逾越应用程序域举行通讯,必需完成通道。如前所述,Remoting供应了IChannel接口,离别包括TcpChannel和HttpChannel两种范例的通道。这两种范例除了机能和序列化数据的花样差别外,完成的体式格局完全一致,因而下面我们就以TcpChannel为例。
注册TcpChannel,起首要在项目中增加援用“System.Runtime.Remoting”,然后using名字空间:System.Runtime.Remoting.Channel.Tcp。代码以下:
TcpChannel channel = new TcpChannel(8022); ChannelServices.RegisterChannel(channel);
在实例化通道对象时,将端口号作为参数通报。然后再挪用静态要领RegisterChannel()来注册该通道对象即可。
2、注册长途对象
注册了通道后,要能激活长途对象,必需在通道中注册该对象。依据激活形式的差别,注册对象的要领也差别。
(1) SingleTon形式
关于WellKnown对象,能够经由过程静态要领RemotingConfiguration.RegisterWellKnownServiceType()来完成:
RemotingConfiguration.RegisterWellKnownServiceType( typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.SingleTon);
(2)SingleCall形式
注册对象的要领基本上和SingleTon形式雷同,只须要将罗列参数WellKnownObjectMode改成SingleCall就能够了。
RemotingConfiguration.RegisterWellKnownServiceType( typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.SingleCall);
客户端完成步骤:
1、注册通道:
TcpChannel channel = new TcpChannel(); ChannelServices.RegisterChannel(channel);
注重在客户端实例化通道时,是挪用的默许组织函数,即没有通报端口号。事实上,这个端口号是缺一不可的,只不过它的指定被放在背面作为了Uri的一部分。
2、取得长途对象。
与效劳器端雷同,差别的激活形式决议了客户端的完成体式格局也将差别。不过这个区分仅仅是WellKnown激活形式和客户端激活形式之间的区分,而关于SingleTon和SingleCall形式,客户端的完成完全雷同。
(1) WellKnown激活形式
要取得效劳器端的着名长途对象,可经由过程Activator历程的GetObject()要领来取得:
ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject( typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");
起首以WellKnown形式激活,客户端取得对象的要领是运用GetObject()。个中参数第一个是长途对象的范例。第二个参数就是效劳器端的uri。如果是http通道,自然是用localhost:8022/ServiceMessage了。由于我是用本地机,所以这里是localhost,你能够用细致的效劳器IP地点来替代它。端口必需和效劳器端的端口一致。背面则是效劳器定义的长途对象效劳名,即ApplicationName属性的内容。
//设置反序列化级别 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full;//支撑一切范例的反序列化,级别很高 //信道端口 IDictionary idic = new Dictionary<string, string>(); idic["name"] = "clientHttp"; idic["port"] = "0"; HttpChannel channel = new HttpChannel(idic, clientProvider, serverProvider);
从上述代码中能够看到注册体式格局有所变化,那是由于客户端注册效劳端的事宜时会报错“不允许范例反序列化”。
另有一个须要注重的是:
ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage"); //RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), "SumMessage", WellKnownObjectMode.Singleton); //挪用体系自动建立,致使拿不到_remotingObject对象的实例化,如许后期绑定事宜就没法操纵下去了,固然也能够直接静态事宜绑定,如许就不须要手动实例化对象了
经由过程该要领手动建立_remotingObject这个对象的实例化。
然后之前讲到了一个SwapObject这个类,这个类的作用是事宜交流。
_remotingObject.ServerToClient +=要领(); //如许由于这个要领是客户端的,效劳端没法挪用,所以须要一个中心转换的 SwapObject swap = new SwapObject();//先建立一个Swap对象 _remotingObject.ServerToClient += swap.ToClient; //然后效劳端事宜发信息给swap,然后swap再经由过程事宜发音讯给客户端,swap是客户端建立的所以能够发送,而swap是效劳端的类,所以效劳端也能辨认,swap起到了中心过渡的作用 swap.SwapServerToClient +=要领();
以上就是C#NetRemoting完成双向通讯的示例代码的细致内容,更多请关注ki4网别的相干文章!