In diesem Artikel wird hauptsächlich C# NetRemoting vorgestellt, um eine bidirektionale Kommunikation zu erreichen. .Net Remoting wird vom Client verwendet, um über Remoting auf den Server zuzugreifen. /www.php .cn/wiki/60.html" target="_blank">Objektusing 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; } }
-Methode referenziert wird, also die Die übergebene Remote-Objektklasse muss MarshalByRefObject erben: MarshalByRefObject ist die Basisklasse für Objekte, die über Proxys zum Austausch von Nachrichten kommunizieren Wenn eine Remote-Anwendung auf ein gemarshalltes Objekt verweist, wird eine Kopie des Objekts über die Remoting-Grenze übergeben, da Sie stattdessen Proxy-Methoden verwenden möchten. Daher müssen Sie MarshallByRefObject erben >Diese Klasse definiert hauptsächlich einige Methoden für den Client zum Auslösen von Ereignissen, ToServer, ToClient, ToLogin, ToExit und einige Ereignisse, Ereignisse, die vom Client an den Server gesendet werden, und Ereignisse, die vom Server an den Client gesendet werden
_nameHash zeichnet nur auf, welcheBenutzer sich angemeldet haben
Der nächste Schritt ist der Client und der Server 🎜>Erster Server:
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>(); } }
Dann Client:
Server-Implementierungsschritte:
1. Kanal registrieren
Um über Anwendungsdomänen hinweg zu kommunizieren, müssen Kanäle implementiert werden. Wie bereits erwähnt, stellt Remoting die IChannel-Schnittstelle bereit, die zwei Arten von Kanälen enthält: TcpChannel und HttpChannel. Abgesehen von der unterschiedlichen Leistung und dem unterschiedlichen Format serialisierter Daten werden diese beiden Typen auf genau die gleiche Weise implementiert, daher nehmen wir im Folgenden TcpChannel als Beispiel. Um TcpChannel zu registrieren, fügen Sie zunächst die Referenz „System.Runtime.Remoting“ zum Projekt hinzu und verwenden Sie dann den Namespace: System.Runtime.Remoting.Channel.Tcp. Der Code lautet wie folgt:
Übergeben Sie beim Instanziieren des Kanalobjekts die Portnummer als Parameter. Rufen Sie dann diestatische
-Methode RegisterChannel() auf, um das Kanalobjekt zu registrieren.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; } }
Nach der Registrierung des Kanals muss das Objekt im Kanal registriert werden, um das Remote-Objekt zu aktivieren. Je nach Aktivierungsmodus ist die Methode zur Registrierung des Objekts unterschiedlich.
(1) SingleTon-Modus
Für das WellKnown-Objekt kann es über die statische Methode RemotingConfiguration.RegisterWellKnownServiceType() implementiert werden: (2)SingleCall-ModusTcpChannel channel = new TcpChannel(8022); ChannelServices.RegisterChannel(channel);
Client-Implementierung
Schritte:
1. Registrierungskanal:
RemotingConfiguration.RegisterWellKnownServiceType( typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.SingleTon);
Beachten Sie, dass beim Instanziieren des Kanals durch den Client der Standard-Konstruktor
aufgerufen wird, d. h. es wird keine Portnummer übergeben. Tatsächlich ist diese Portnummer unverzichtbar, ihre Angabe wird jedoch später als Teil der Uri platziert.2. Erhalten Sie das Remote-Objekt
.
RemotingConfiguration.RegisterWellKnownServiceType( typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.SingleCall);
(1) WellKnown-Aktivierungsmodus
Um das bekannte Remote-Objekt auf der Serverseite abzurufen, können Sie es über die GetObject()-Methode des Aktivators abrufen Prozess: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 +=方法();
Das obige ist der detaillierte Inhalt vonC#NetRemoting implementiert die gemeinsame Nutzung von Beispielcode für die bidirektionale Kommunikation. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!