亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C# NetRemoting實現雙向通信

2019-10-29 21:12:06
字體:
來源:轉載
供稿:網友

閑來無事想玩玩雙向通信,實現類似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通道,自然是用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 +=方法();

以上是兩天.Net Remoting的學習結果。

最后附上源碼:NetRemoting.rar

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到c#教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久亚洲影音av资源网| 亚洲成人网久久久| 在线视频精品一| 亚洲中国色老太| 人人爽久久涩噜噜噜网站| 亚洲肉体裸体xxxx137| 国产精品色午夜在线观看| 日韩成人免费视频| 中文日韩在线视频| 成人在线播放av| 亚洲男人天堂2024| 国产精品视频网站| 91成人福利在线| 色综合天天综合网国产成人网| 欧美中文字幕精品| 日韩中文在线观看| 91久久精品在线| 久久久久久亚洲精品不卡| 国产精品视频免费观看www| 国产综合色香蕉精品| 伊人成人开心激情综合网| 91网站在线看| 中文字幕日韩在线视频| 国产精品第1页| 亚洲精品理论电影| 一区二区三区天堂av| 91高潮精品免费porn| 欧美xxxx做受欧美| 国产精品久久久久久久久影视| 欧美日韩中文字幕日韩欧美| 日韩精品极品在线观看| 中文字幕亚洲一区| 亚洲精品av在线| 精品久久久久久中文字幕大豆网| 日韩av电影手机在线| 亚洲精品xxx| 97久久精品人搡人人玩| 性夜试看影院91社区| 国产精品美女午夜av| 日本精品久久久久影院| 亚洲欧美中文另类| 久久亚洲精品小早川怜子66| 亚洲18私人小影院| 亚洲 日韩 国产第一| 久久久国产一区二区| 日韩国产在线播放| 欧美性精品220| 欧美xxxx18性欧美| 日韩女优人人人人射在线视频| 亚洲天堂网站在线观看视频| 久久久久久久网站| 亚洲电影第1页| 2019av中文字幕| 国产自摸综合网| 欧美中文字幕在线| 久久精品夜夜夜夜夜久久| 成人国产精品免费视频| 精品免费在线视频| 欧美激情高清视频| 韩曰欧美视频免费观看| 国产综合视频在线观看| 亚洲人成欧美中文字幕| 5252色成人免费视频| 成人黄色av免费在线观看| 亚洲欧美日韩在线一区| 亚洲人午夜精品免费| 国产精品扒开腿做| 97国产一区二区精品久久呦| 亚洲人成电影网站| 欧美成人精品h版在线观看| 久久久久久久999精品视频| 琪琪亚洲精品午夜在线| 国产91成人video| 国产欧美日韩91| 国产精品久久久久av免费| 日韩欧美在线字幕| 亚洲人成人99网站| 国产精品欧美日韩一区二区| 成人黄色av播放免费| 亚洲人成网站777色婷婷| 久久视频国产精品免费视频在线| 欧美大片在线看| 精品久久久久久中文字幕一区奶水| 成人久久久久久久| 在线国产精品播放| 欧美性猛交xxxx免费看| 日韩精品高清在线| 美女少妇精品视频| 性欧美xxxx视频在线观看| 中文日韩在线观看| 国产美女直播视频一区| 97视频在线观看免费| 91精品在线看| 日韩理论片久久| 精品视频www| 国产精品狼人色视频一区| 亚洲国产精久久久久久| 中文字幕国产日韩| 日本精品免费一区二区三区| 亚洲精品一区二三区不卡| 最新日韩中文字幕| 法国裸体一区二区| 日韩亚洲国产中文字幕| 亚洲sss综合天堂久久| 色阁综合伊人av| 91黄色8090| 在线亚洲男人天堂| 久久久久国产精品一区| 黄色成人在线播放| 久久99热精品| 欧美色另类天堂2015| 午夜精品在线视频| 色综合老司机第九色激情| 亚洲视频在线观看视频| 日韩国产欧美区| 91在线观看免费| 日韩欧美视频一区二区三区| 国产欧美日韩精品丝袜高跟鞋| 日韩av123| 国产精品午夜一区二区欲梦| 色偷偷av亚洲男人的天堂| 欧美日本精品在线| 日韩欧美成人网| 国产精品视频资源| 一区二区成人av| 亚洲日韩中文字幕| 国产欧美精品久久久| 8090成年在线看片午夜| 亚洲石原莉奈一区二区在线观看| 九色91av视频| 国产日产久久高清欧美一区| 色偷偷噜噜噜亚洲男人| 日韩一区二区久久久| 日韩在线www| 欧美日韩国产黄| 欧美激情视频免费观看| 久久人91精品久久久久久不卡| 成人女保姆的销魂服务| 日韩成人在线电影网| 亚洲成人黄色在线观看| 少妇高潮 亚洲精品| 亚洲欧洲一区二区三区久久| 欧美在线视频网| 日韩中文视频免费在线观看| 中文字幕欧美亚洲| 人九九综合九九宗合| 国产在线98福利播放视频| 成年人精品视频| 久久影院资源站| 久久久久久久久久久亚洲| 日本韩国在线不卡| 国产精品日日摸夜夜添夜夜av| 欧美视频在线视频| 97视频在线观看免费高清完整版在线观看| 日韩欧美在线播放| 国产精品激情av电影在线观看| 成人免费视频97| 伊人久久久久久久久久久| 91av在线网站| 久久久久久91香蕉国产| 日韩精品在线免费| 国产日韩一区在线| 日本欧美中文字幕| 精品无人国产偷自产在线|