C NetRemoting realizes two way communication

  • 2021-12-12 05:22:03
  • OfStack

I want to play two-way communication when I have nothing to do, and realize the function of sending messages to each other like QQ. So I started to learn. Net Remoting.

. Net Remoting is the client through Remoting, access channel to obtain server object, and then through the proxy to resolve the client object to achieve communication. That is to say, the object is created by the server.

Code first

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>
    ///  Addition operation 
    /// </summary>
    /// <param name="x1"> Parameter 1</param>
    /// <param name="x2"> Parameter 2</param>
    /// <returns></returns>
    string SUM(int x1, int x2);
    /// <summary>
    ///  Getting a list of server-side events 
    /// </summary>
    Delegate[] GetServerEventList();
    /// <summary>
    ///  Send a message 
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    void ToServer(object info, string toName);
    /// <summary>
    ///  Receive 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>
  ///  Client sends message 
  /// </summary>
  /// <param name="info"> Information </param>
  /// <param name="toName"> To whom, "" It means everyone, null Indicates that no receiving server receives it itself, and others indicate that someone is specified </param>
  public delegate void SendHandler(object info, string toName);
  /// <summary>
  ///  Client receives message 
  /// </summary>
  /// <param name="info"> Information </param>
  /// <param name="toName"> To whom, "" It means everyone, null Indicates that no receiving server receives it itself, and others indicate that someone is specified </param>
  public delegate void ReceiveHandler(object info, string toName);
  /// <summary>
  ///  User information event 
  /// </summary>
  /// <param name="name"> User 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>
    ///  Receive 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 1 interface and 1 delegate, and there is no substantive thing.

The second class defines the events and methods of ToClient in the previous interface class, which will be discussed later.

Then there are the substantive data classes that integrate 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>
    ///  Send an event 
    /// </summary>
    public event SendHandler ClientToServer
    {
      add { _send += value; }
      remove { _send -= value; }
    }
    /// <summary>
    ///  Receive message event 
    /// </summary>
    public event ReceiveHandler ServerToClient;
    /// <summary>
    ///  Send an event 
    /// </summary>
    public event UserChangedHandler Login
    {
      add { _login += value; }
      remove { _login -= value; }
    }
    /// <summary>
    ///  Send an event 
    /// </summary>
    public event UserChangedHandler Exit
    {
      add { _exit += value; }
      remove { _exit -= value; }
    }
    /// <summary>
    ///  Addition operation 
    /// </summary>
    /// <param name="x1"> Parameter 1</param>
    /// <param name="x2"> Parameter 2</param>
    /// <returns></returns>
    public string SUM(int x1, int x2)
    {
      return x1 + "+" + x2 + "=" + (x1 + x2);
    }
    /// <summary>
    ///  Bind the event method of sending messages from server to client 
    /// </summary>
    /// <param name="receive"> Receive event </param>
    public Delegate[] GetServerEventList()
    {
      return this.ServerToClient.GetInvocationList();
    }
    /// <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 a message 
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    public void ToClient(object info, string toName)
    {
      if (_receive != null)
        _receive(info, toName);
    }
    /// <summary>
    ///  Login 
    /// </summary>
    /// <param name="name"> User name </param>
    public void ToLogin(string name)
    {
      if (!_nameHash.Contains(name))
      {
        _nameHash.Add(name);
        if (_login != null)
          _login(name);
      }
      else
      { throw new Exception(" User already exists "); }
    }
    /// <summary>
    ///  Quit 
    /// </summary>
    /// <param name="name"> User 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

Because Remoting passes objects by reference, the remote object class passed must inherit from MarshalByRefObject. MSDN describes MarshalByRefObject as the base class for objects that communicate across application domain boundaries by exchanging messages using proxies. Objects that do not inherit from MarshalByRefObject are implicitly marshaled by value. When a remote application references a marshaled object by value, a copy of the object is passed across remoting boundaries. Because you want to communicate using a proxy method instead of a replica method, you need to inherit MarshallByRefObject.

This class mainly defines 1 method for client-side triggering events, ToServer, ToClient, ToLogin, ToExit and 1 event, events from client-side to server-side, and events from server-side to client-side.

_ nameHash just records which users are logged in.

Then there is the client and server.

First, the server side:


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>
    ///  Registration channel 
    /// </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 Program 
      /* Returns the given  System.MarshalByRefObject  Converts to one with the specified  URI  Adj.  System.Runtime.Remoting.ObjRef  Gets or sets an instance of the. 
       ObjRef  Stores all the information needed to build the proxy to communicate with remote objects. */
      ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage");//b Program 
      _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 + "  Login " + "\r\n"); }));
      };
      _remotingObject.Exit += (name) =>
      {
        rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(name + "  Quit " + "\r\n"); }));
      };
    }
    /// <summary>
    ///  Logout channel 
    /// </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>
    ///  Broadcast message 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnSend_Click(object sender, EventArgs e)
    {
      SendToClient(txtSend.Text, txtName.Text);
    }
    /// <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;
      //    receive.BeginInvoke(info, toName, null, null);
      //  }
      //  catch
      //  { }
      // }
      _remotingObject.ToClient(txtSend.Text, txtName.Text);
    }
    /// <summary>
    ///  Initialization 
    /// </summary>
    private void Initialize()
    {
      // Setting the deserialization level  
      BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
      BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
      serverProvider.TypeFilterLevel = TypeFilterLevel.Full;// All types of deserialization are supported at a 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 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>
    ///  Registration channel 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Client_Load(object sender, EventArgs e)
    {
      try
      {
        // Setting the deserialization level  
        BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
        BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
        serverProvider.TypeFilterLevel = TypeFilterLevel.Full;// All types of deserialization are supported at a high level  
        // Channel port  
        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>
    ///  Login 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnLogin_Click(object sender, EventArgs e)
    {
      try
      {
        if (txtLogin.Text == "")
          throw new Exception(" User name must not be empty ");
        _remotingObject.ToLogin(txtLogin.Text);
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }
    /// <summary>
    ///  Quit 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Client_FormClosing(object sender, FormClosingEventArgs e)
    {
      try
      {
        _remotingObject.ToExit(txtLogin.Text);
      }
      catch
      { }
    }
    /// <summary>
    ///  Send 
    /// </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 side implementation steps:

1. Registration channel

To communicate across application domains, channels must be implemented. As mentioned earlier, Remoting provides the IChannel interface, which contains two types of channels, TcpChannel and HttpChannel. These two types are implemented in exactly the same way except for different performance and serialized data formats, so let's take TcpChannel as an example.

To register TcpChannel, first add the reference "System. Runtime. Remoting" to the project, and then the using namespace: System. Runtime. Remoting. Channel. Tcp. The code is as follows:


TcpChannel channel = new TcpChannel(8022);
ChannelServices.RegisterChannel(channel);

When the channel object is instantiated, the port number is passed as a parameter. Then call the static method RegisterChannel () to register the channel object.

2. Register a remote object

After registering a channel, in order to activate a remote object, the object must be registered in the channel. Depending on the activation mode, the method of registering objects varies.

(1) SingleTon mode

For the WellKnown object, this can be done with the static method RemotingConfiguration. RegisterWellKnownServiceType ():


RemotingConfiguration.RegisterWellKnownServiceType(
        typeof(ServerRemoteObject.ServerObject),
        "ServiceMessage",WellKnownObjectMode.SingleTon);

(2) SingleCall mode

The method of registering objects is basically the same as the SingleTon schema, just 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);

Note that when the client instantiates the channel, it is the default constructor that is called, that is, no port number is passed. In fact, this port number is not required, except that it is specified as part 1 of Uri.

2. Get the remote object.

As on the server side, different activation modes determine that the implementation of the client side will be different. However, this difference is only between the WellKnown activation mode and the client activation mode, and the client implementation is exactly the same for SingleTon and SingleCall modes.

(1) WellKnown activation mode

To get well-known remote objects on the server side, you can get them through the GetObject () method of the Activator process:


ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(
       typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");

First activated in WellKnown mode, the client gets the object by using GetObject (). The first parameter is the type of the remote object. The second parameter is uri on the server side. If it is an http channel, it is naturally http://localhost: 8022/ServiceMessage. Since I am using a local machine, this is localhost, and you can replace it with the specific server IP address. Port must match Port 1 on the server side. This is followed by the remote object service name defined by the server, which is the content of the ApplicationName attribute.


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>
    ///  Receive 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;
  } 
}

0

From the above code, you can see that the registration method has changed, because when the client registers the server event, it will report an error of "type deserialization is not allowed".

Another thing to note is:


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>
    ///  Receive 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;
  } 
}

1

This method manually creates an instantiation of the object _ remotingObject.

Then I talked about an SwapObject class, which is used for event exchange.


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>
    ///  Receive 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;
  } 
}

2

The above is the learning result of Net Remoting in two days.

Finally, attach the source code: NetRemoting_jb51.rar


Related articles: