This article mainly introduces C# NetRemoting realizes two-way communication. .Net Remoting is used by the client to access the channel to obtain the server through Remotingobject, and then resolve it into a client object through the proxy to achieve communication
I have nothing to do and want to play with two-way communication, achieving something like QQ's function of sending messages to each other. So I started to learn .Net Remoting.
.Net Remoting is implemented by the client accessing the channel to obtain the server object through Remoting, and then parsing it into the client object through the proxy. Communication. In other words, the object is created by the server.
The first is the ICommand library
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; } }
The first class is to define some
The interface, and some delegates, have nothing substantial. The second class defines the ToClient events and methods in the previous interface class, which will be discussed later. ##Then there is the substantive data class that integrates the ICommand interface
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>(); } }
This class integrates MarshalByRefObject
Since the object passed by Remoting is
referenced, so The passed remote object class must
InheritMarshalByRefObject. MSDN's description of MarshalByRefObject is: MarshalByRefObject is the base class for objects that communicate across application domain boundaries by using proxies. Objects that do not inherit from MarshalByRefObject. Marshaling by value is implicit. When a remote application references an object that is marshaled by value, a copy of the object is passed across the remoting boundary, which is required because you want to communicate using proxy methods instead of copy methods. Inherits MarshallByRefObject. This class mainly defines some methods for the client to trigger events, ToServer, ToClient, ToLogin, ToExit and some events, events sent by the client to the server, and events sent by the server to the client. Event. ##_nameHash just records which
users have logged in. The next step is the client and server
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; } }
Server implementation steps:
1. Registration channel
To communicate across application domains, channels must be implemented. As mentioned before, Remoting provides the IChannel interface, which contains two types of channels: TcpChannel and HttpChannel. Except for the different performance and format of serialized data, these two types are implemented in exactly the same way, so below we will take TcpChannel as an example.
To register TcpChannel, first add the reference "System.Runtime.Remoting" to the project, and then use the namespace: System.Runtime.Remoting.Channel.Tcp. The code is as follows:TcpChannel channel = new TcpChannel(8022); ChannelServices.RegisterChannel(channel);
2. Register the remote object
(1) SingleTon mode
For the WellKnown object, it can be implemented through the static method RemotingConfiguration.RegisterWellKnownServiceType():RemotingConfiguration.RegisterWellKnownServiceType( typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.SingleTon);
( 2)SingleCall mode
The method of registering an object is basically the same as the SingleTon mode. You only need to change the enumeration parameter WellKnownObjectMode to SingleCall.RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ServerRemoteObject.ServerObject),
"ServiceMessage",WellKnownObjectMode.SingleCall);
Client implementation
Steps:1. Registration channel: TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel);
2. Obtain the remote object
.(1) WellKnown activation mode
To obtain the well-known remote object on the server side, you can obtain it through the GetObject() method of the Activator process:ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject( typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");
首先以WellKnown模式激活,客户端获得对象的方法是使用GetObject()。其中参数第一个是远程对象的类型。第二个参数就是服务器端的uri。如果是http通道,自然是用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 +=方法();
The above is the detailed content of C#NetRemoting implements sample code sharing for two-way communication. For more information, please follow other related articles on the PHP Chinese website!