I want to play two-way communication when I have nothing to do to realize the function of sending messages to each other similar to QQ. So I started to learn.Net Remoting.
.Net Remoting is used by the client to access the channel through Remoting to obtain server objects, and then to resolve them into client objects through proxy communication. That is to say, the object is created by the server.
Add the code first
First is the ICommand library
using System; using ; using ; using ; namespace ICommand { public interface IRemotingObject { event SendHandler ClientToServer; event ReceiveHandler ServerToClient; event UserChangedHandler Login; event UserChangedHandler Exit; /// <summary> /// Addition operation /// </summary> /// <param name="x1">param 1</param> /// <param name="x2">param 2</param> /// <returns></returns> string SUM(int x1, int x2); /// <summary> /// Get the server event list /// </summary> Delegate[] GetServerEventList(); /// <summary> /// Send a message /// </summary> /// <param name="info"></param> /// <param name="toName"></param> void ToServer(object info, string toName); /// <summary> /// Accept information /// </summary> /// <param name="info"></param> /// <param name="toName"></param> void ToClient(object info, string toName); void ToLogin(string name); void ToExit(string name); } /// <summary> /// The client sends a message /// </summary> /// <param name="info">Information</param> /// <param name="toName">Send to whom, "" means everyone, null means no receiving server receives it itself, others means specifying someone</param> public delegate void SendHandler(object info, string toName); /// <summary> /// The client receives messages /// </summary> /// <param name="info">Information</param> /// <param name="toName">Send to whom, "" means everyone, null means no receiving server receives it itself, others means specifying someone</param> public delegate void ReceiveHandler(object info, string toName); /// <summary> /// User information event /// </summary> /// <param name="name">username</param> public delegate void UserChangedHandler(string name); }
using System; using ; using ; using ; namespace ICommand { public class SwapObject : MarshalByRefObject { public event ReceiveHandler SwapServerToClient { add { _receive += value; } remove { _receive -= value; } } /// <summary> /// Accept information /// </summary> /// <param name="info"></param> /// <param name="toName"></param> public void ToClient(object info, string toName) { if (_receive != null) _receive(info, toName); } //Infinite life cycle public override object InitializeLifetimeService() { return null; } private ReceiveHandler _receive; } }
The first class is to define some interfaces and some delegations, without substantive things.
The second class defines the events and methods of ToClient 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 ; using ; using ; using ICommand; namespace NetRemoting { public class RemotingObject : MarshalByRefObject, IRemotingObject { /// <summary> /// Send events /// </summary> public event SendHandler ClientToServer { add { _send += value; } remove { _send -= value; } } /// <summary> /// Receive message events /// </summary> public event ReceiveHandler ServerToClient; /// <summary> /// Send events /// </summary> public event UserChangedHandler Login { add { _login += value; } remove { _login -= value; } } /// <summary> /// Send events /// </summary> public event UserChangedHandler Exit { add { _exit += value; } remove { _exit -= value; } } /// <summary> /// Addition operation /// </summary> /// <param name="x1">param 1</param> /// <param name="x2">param 2</param> /// <returns></returns> public string SUM(int x1, int x2) { return x1 + "+" + x2 + "=" + (x1 + x2); } /// <summary> /// The event method of binding the server to send messages to the client /// </summary> /// <param name="receive">Receive events</param> public Delegate[] GetServerEventList() { return (); } /// <summary> /// Send a message /// </summary> /// <param name="info"></param> /// <param name="toName"></param> public void ToServer(object info, string toName) { if (_send != null) _send(info, toName); } /// <summary> /// Receive message /// </summary> /// <param name="info"></param> /// <param name="toName"></param> public void ToClient(object info, string toName) { if (_receive != null) _receive(info, toName); } /// <summary> /// Log in /// </summary> /// <param name="name">username</param> public void ToLogin(string name) { if (!_nameHash.Contains(name)) { _nameHash.Add(name); if (_login != null) _login(name); } else { throw new Exception("The user already exists"); } } /// <summary> /// quit /// </summary> /// <param name="name">username</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 objects passed by Remoting are referenced, the passed remote object class must inherit the MarshalByRefObject. MSDN's description of MarshalByRefObject is: MarshalByRefObject is the base class of objects that communicate across application domain boundaries by using proxy exchange messages. Objects that are not inherited from MarshalByRefObject are marshaled implicitly by value. When a remote application references an object marshaled by value, a copy of the object is passed across the remote processing boundary. Because you want to communicate using a proxy method instead of a replica method, you need to inherit MarshallByRefObject.
This class mainly defines some methods used to trigger events on the client, ToServer, ToClient, ToLogin, ToExit and some events, events sent by the client to the server, and events sent by the server to the client.
_nameHash just records which users are logged in.
Next is the client and server.
First of all, the server side:
using System; using ; using ; using ; using ; using ; using ; using ; using ; using ; using ; using NetRemoting; using ; using ; using ICommand; namespace NetRemotingServer { public partial class Server : Form { public Server() { InitializeComponent(); Initialize(); } /// <summary> /// Register channel /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Server_Load(object sender, EventArgs e) { (_channel, false); //(typeof(RemotingObject), "SumMessage", ); //aScheme /*Convert the given Partition to an instance of the class with the specified URI. ObjRef: stores all the information needed to generate the agent to communicate with the remote object. */ ObjRef objRef = (_remotingObject, "SumMessage");//Scheme b _remotingObject.ClientToServer += (info, toName) => { ((MethodInvoker)(() => { (() + "\r\n"); })); SendToClient(info, toName); }; _remotingObject.Login += (name) => { ((MethodInvoker)(() => { (name + " Log in" + "\r\n"); })); }; _remotingObject.Exit += (name) => { ((MethodInvoker)(() => { (name + " quit" + "\r\n"); })); }; } /// <summary> /// Log out of channel /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Server_FormClosing(object sender, FormClosingEventArgs e) { if (_channel != null) { _channel.StopListening(null); (_channel); } } /// <summary> /// Broadcast message /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) { SendToClient(, ); } /// <summary> /// Send a message to the client /// </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; // (info, toName, null, null); // } // catch // { } // } _remotingObject.ToClient(, ); } /// <summary> /// Initialization /// </summary> private void Initialize() { //Set the deserialization level BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); = ;//Support all types of deserialization, very high level 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; } }
Then the client:
using System; using ; using ; using ; using ; using ; using ; using ; using ; using ; using ; using ICommand; using ; using ; namespace NetRemotingClient { public partial class Client : Form { public Client() { InitializeComponent(); } /// <summary> /// Register channel /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Client_Load(object sender, EventArgs e) { try { //Set the deserialization level BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); = ;//Support all types of deserialization, very high level //Channel port IDictionary idic = new Dictionary<string, string>(); idic["name"] = "clientHttp"; idic["port"] = "0"; HttpChannel channel = new HttpChannel(idic, clientProvider, serverProvider); (channel, false); _remotingObject = (IRemotingObject)(typeof(IRemotingObject), "http://localhost:8022/SumMessage"); //_remotingObject.ServerToClient += (info, toName) => { (info + "\r\n"); }; SwapObject swap = new SwapObject(); _remotingObject.ServerToClient += ; += (info, toName) => { ((MethodInvoker)(() => { if (toName == || toName == "") (info + "\r\n"); })); }; } catch (Exception ex) { (); } } /// <summary> /// Log in /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnLogin_Click(object sender, EventArgs e) { try { if ( == "") throw new Exception("Username must not be empty"); _remotingObject.ToLogin(); } catch (Exception ex) { (); } } /// <summary> /// quit /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Client_FormClosing(object sender, FormClosingEventArgs e) { try { _remotingObject.ToExit(); } catch { } } /// <summary> /// send /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) { //(_remotingObject.SUM(2, 4) + "\r\n"); _remotingObject.ToServer(, ); } private IRemotingObject _remotingObject; } }
Server-side implementation steps:
1. Register channel
To communicate across application domains, channels must be implemented. As mentioned earlier, Remoting provides an IChannel interface, which includes two types of channels: TcpChannel and HttpChannel. In addition to the different performance and formats of serialized data, the implementation methods of these two types are completely consistent. Therefore, let’s take TcpChannel as an example.
To register a TcpChannel, first you need to add the reference "" in the project, and then use namespace:. The code is as follows:
TcpChannel channel = new TcpChannel(8022); (channel);
When instantiating a channel object, pass the port number as a parameter. Then call the static method RegisterChannel() to register the channel object.
2. Register remote objects
After registering a channel, in order to be able to activate a remote object, the object must be registered in the channel. Depending on the activation mode, the method of registering objects is also different.
(1) SingleTon mode
For WellKnown objects, they can be implemented through static method():
( typeof(), "ServiceMessage",);
(2) SingleCall mode
The method of registering objects is basically the same as SingleTon mode, you just need to change the enumeration parameter WellKnownObjectMode to SingleCall.
( typeof(), "ServiceMessage",);
Client implementation steps:
1. Registration channel:
TcpChannel channel = new TcpChannel(); (channel);
Note that when the client instantiates the channel, it is the default constructor called, that is, no port number is passed. In fact, this port number is indispensable, except that its specification is placed behind as part of Uri.
2. Obtain remote objects。
The same as the server side, different activation modes determine that the implementation method of the client will also be different. However, this difference is just the difference between WellKnown activation mode and client activation mode, while the client implementation is exactly the same for SingleTon and SingleCall modes.
(1) WellKnown activation mode
To obtain a well-known remote object on the server side, you can obtain it through the GetObject() method of the Activator process:
serverObj = ()( typeof(), "tcp://localhost:8080/ServiceMessage");
First, it is activated in WellKnown mode. The way the client gets the object is to use GetObject(). The first parameter is the type of the remote object. The second parameter is the server-side uri. If it is an http channel, you will naturally use http://localhost:8022/ServiceMessage. Because I am using a local machine, here is localhost, you can use the specific server IP address instead. The port must be consistent with the port on the server side. The following is the remote object service name defined by the server, that is, the content of the ApplicationName property.
//Set the deserialization level BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); = ;//Support all types of deserialization, very high level //Channel port IDictionary idic = new Dictionary<string, string>(); idic["name"] = "clientHttp"; idic["port"] = "0"; HttpChannel channel = new HttpChannel(idic, clientProvider, serverProvider);
From the above code, we can see that the registration method has changed, because the client will report an error "Type deserialization is not allowed" when registering events on the server.
Another thing to note is:
ObjRef objRef = (_remotingObject, "SumMessage"); //(typeof(RemotingObject), "SumMessage", ); //Calling the system to create automatically,It can't get it_remotingObjectInstantiation of objects,In this way, the later binding event cannot be operated,Of course, you can also directly bind static events,This way, there is no need to manually instantiate the object
Use this method to manually create an instantiation of this object _remotingObject.
Then I talked about a SwapObject class before, and the function of this class is event exchange.
_remotingObject.ServerToClient +=method(); //In this way, because this method is client-side and cannot be called by the server, it is necessary to convert an intermediate conversion. SwapObject swap = new SwapObject();// Create a Swap object first _remotingObject.ServerToClient += ;//Then the server event sends a message to swap, and then swap sends a message to the client through the event. swap is created by the client so it can be sent, and swap is a class on the server, so the server can also recognize it. swap plays an intermediate transition role. +=method();
The above are the two-day learning results of .Net Remoting.
Finally, the source code is attached:NetRemoting_jb51.rar
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.