C# NetRemoting實現(xiàn)雙向通信
閑來無事想玩玩雙向通信,實現(xiàn)類似QQ的互發(fā)消息的功能。于是乎開始學(xué)習(xí).Net Remoting.
.Net Remoting 是由客戶端通過Remoting,訪問通道以獲得服務(wù)端對象,再通過代理解析為客戶端對象來實現(xiàn)通信的。也就是說對象是由服務(wù)端創(chuàng)建的。
先上代碼
首先是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">參數(shù)1</param>
/// <param name="x2">參數(shù)2</param>
/// <returns></returns>
string SUM(int x1, int x2);
/// <summary>
/// 獲取服務(wù)端事件列表
/// </summary>
Delegate[] GetServerEventList();
/// <summary>
/// 發(fā)送消息
/// </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>
/// 客戶端發(fā)送消息
/// </summary>
/// <param name="info">信息</param>
/// <param name="toName">發(fā)送給誰,""表示所有人,null表示沒有接收服務(wù)器自己接收,其他表示指定某人</param>
public delegate void SendHandler(object info, string toName);
/// <summary>
/// 客戶端接收消息
/// </summary>
/// <param name="info">信息</param>
/// <param name="toName">發(fā)送給誰,""表示所有人,null表示沒有接收服務(wù)器自己接收,其他表示指定某人</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;
}
}
第一個類就是定義一些接口,和一些委托,沒有實質(zhì)性的東西。
第二個類是定義了上一個接口類中的ToClient的事件和方法,作用之后會講到。
然后就是集成ICommand接口的實質(zhì)性的數(shù)據(jù)類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICommand;
namespace NetRemoting
{
public class RemotingObject : MarshalByRefObject, IRemotingObject
{
/// <summary>
/// 發(fā)送事件
/// </summary>
public event SendHandler ClientToServer
{
add { _send += value; }
remove { _send -= value; }
}
/// <summary>
/// 接收消息事件
/// </summary>
public event ReceiveHandler ServerToClient;
/// <summary>
/// 發(fā)送事件
/// </summary>
public event UserChangedHandler Login
{
add { _login += value; }
remove { _login -= value; }
}
/// <summary>
/// 發(fā)送事件
/// </summary>
public event UserChangedHandler Exit
{
add { _exit += value; }
remove { _exit -= value; }
}
/// <summary>
/// 加法運算
/// </summary>
/// <param name="x1">參數(shù)1</param>
/// <param name="x2">參數(shù)2</param>
/// <returns></returns>
public string SUM(int x1, int x2)
{
return x1 + "+" + x2 + "=" + (x1 + x2);
}
/// <summary>
/// 綁定服務(wù)端向客戶端發(fā)送消息的事件方法
/// </summary>
/// <param name="receive">接收事件</param>
public Delegate[] GetServerEventList()
{
return this.ServerToClient.GetInvocationList();
}
/// <summary>
/// 發(fā)送消息
/// </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傳遞的對象是以引用的方式,因此所傳遞的遠(yuǎn)程對象類必須繼承MarshalByRefObject。MSDN對MarshalByRefObject的說明是:MarshalByRefObject 是那些通過使用代理交換消息來跨越應(yīng)用程序域邊界進(jìn)行通信的對象的基類。不是從 MarshalByRefObject 繼承的對象會以隱式方式按值封送。當(dāng)遠(yuǎn)程應(yīng)用程序引用一個按值封送的對象時,將跨越遠(yuǎn)程處理邊界傳遞該對象的副本。因為您希望使用代理方法而不是副本方法進(jìn)行通信,因此需要繼承MarshallByRefObject。
該類主要是定義了一些方法用于客戶端觸發(fā)事件,ToServer,ToClient,ToLogin,ToExit以及一些事件,客戶端發(fā)向服務(wù)端的事件,和服務(wù)端發(fā)向客戶端的事件。
_nameHash 只是記錄有哪些用戶登錄了。
接下去就是客戶端和服務(wù)端了。
首先服務(wù)端:
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 轉(zhuǎn)換為具有指定 URI 的 System.Runtime.Remoting.ObjRef 類的實例。
ObjRef :存儲生成代理以與遠(yuǎn)程對象通信所需要的所有信息。*/
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>
/// 發(fā)送消息到客戶端
/// </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()
{
//設(shè)置反序列化級別
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
{
//設(shè)置反序列化級別
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>
/// 發(fā)送
/// </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;
}
}
服務(wù)端實現(xiàn)步驟:
1、注冊通道
要跨越應(yīng)用程序域進(jìn)行通信,必須實現(xiàn)通道。如前所述,Remoting提供了IChannel接口,分別包含TcpChannel和HttpChannel兩種類型的通道。這兩種類型除了性能和序列化數(shù)據(jù)的格式不同外,實現(xiàn)的方式完全一致,因此下面我們就以TcpChannel為例。
注冊TcpChannel,首先要在項目中添加引用“System.Runtime.Remoting”,然后using名字空間:System.Runtime.Remoting.Channel.Tcp。代碼如下:
TcpChannel channel = new TcpChannel(8022); ChannelServices.RegisterChannel(channel);
在實例化通道對象時,將端口號作為參數(shù)傳遞。然后再調(diào)用靜態(tài)方法RegisterChannel()來注冊該通道對象即可。
2、注冊遠(yuǎn)程對象
注冊了通道后,要能激活遠(yuǎn)程對象,必須在通道中注冊該對象。根據(jù)激活模式的不同,注冊對象的方法也不同。
(1) SingleTon模式
對于WellKnown對象,可以通過靜態(tài)方法RemotingConfiguration.RegisterWellKnownServiceType()來實現(xiàn):
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ServerRemoteObject.ServerObject),
"ServiceMessage",WellKnownObjectMode.SingleTon);
(2)SingleCall模式
注冊對象的方法基本上和SingleTon模式相同,只需要將枚舉參數(shù)WellKnownObjectMode改為SingleCall就可以了。
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ServerRemoteObject.ServerObject),
"ServiceMessage",WellKnownObjectMode.SingleCall);
客戶端實現(xiàn)步驟:
1、注冊通道:
TcpChannel channel = new TcpChannel(); ChannelServices.RegisterChannel(channel);
注意在客戶端實例化通道時,是調(diào)用的默認(rèn)構(gòu)造函數(shù),即沒有傳遞端口號。事實上,這個端口號是缺一不可的,只不過它的指定被放在后面作為了Uri的一部分。
2、獲得遠(yuǎn)程對象。
與服務(wù)器端相同,不同的激活模式?jīng)Q定了客戶端的實現(xiàn)方式也將不同。不過這個區(qū)別僅僅是WellKnown激活模式和客戶端激活模式之間的區(qū)別,而對于SingleTon和SingleCall模式,客戶端的實現(xiàn)完全相同。
(1) WellKnown激活模式
要獲得服務(wù)器端的知名遠(yuǎn)程對象,可通過Activator進(jìn)程的GetObject()方法來獲得:
ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(
typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");
首先以WellKnown模式激活,客戶端獲得對象的方法是使用GetObject()。其中參數(shù)第一個是遠(yuǎn)程對象的類型。第二個參數(shù)就是服務(wù)器端的uri。如果是http通道,自然是用http://localhost:8022/ServiceMessage了。因為我是用本地機,所以這里是localhost,你可以用具體的服務(wù)器IP地址來代替它。端口必須和服務(wù)器端的端口一致。后面則是服務(wù)器定義的遠(yuǎn)程對象服務(wù)名,即ApplicationName屬性的內(nèi)容。
//設(shè)置反序列化級別
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);
從上述代碼中可以看到注冊方式有所變化,那是因為客戶端注冊服務(wù)端的事件時會報錯“不允許類型反序列化”。
還有一個需要注意的是:
ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage"); //RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), "SumMessage", WellKnownObjectMode.Singleton); //調(diào)用系統(tǒng)自動創(chuàng)建,導(dǎo)致拿不到_remotingObject對象的實例化,這樣后期綁定事件就無法操作下去了,當(dāng)然也可以直接靜態(tài)事件綁定,這樣就不需要手動實例化對象了
通過該方法手動創(chuàng)建_remotingObject這個對象的實例化。
然后之前講到了一個SwapObject這個類,這個類的作用是事件交換。
_remotingObject.ServerToClient +=方法(); //這樣因為這個方法是客戶端的,服務(wù)端無法調(diào)用,所以需要一個中間轉(zhuǎn)換的 SwapObject swap = new SwapObject();//先創(chuàng)建一個Swap對象 _remotingObject.ServerToClient += swap.ToClient;//然后服務(wù)端事件發(fā)信息給swap,然后swap再通過事件發(fā)消息給客戶端,swap是客戶端創(chuàng)建的所以可以發(fā)送,而swap是服務(wù)端的類,所以服務(wù)端也能識別,swap起到了中間過渡的作用 swap.SwapServerToClient +=方法();
以上是兩天.Net Remoting的學(xué)習(xí)結(jié)果。
最后附上源碼:NetRemoting_jb51.rar
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:C#無損轉(zhuǎn)換Image為Icon的方法
欄 目:C#教程
本文標(biāo)題:C# NetRemoting實現(xiàn)雙向通信
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/5808.html
您可能感興趣的文章
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新聞效果的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已安裝軟件變化的方法
- 01-10C#實現(xiàn)多線程下載文件的方法
- 01-10C#實現(xiàn)Winform中打開網(wǎng)頁頁面的方法
- 01-10C#實現(xiàn)遠(yuǎn)程關(guān)閉計算機或重啟計算機的方法
- 01-10C#自定義簽名章實現(xiàn)方法
- 01-10C#文件斷點續(xù)傳實現(xiàn)方法
- 01-10winform實現(xiàn)創(chuàng)建最前端窗體的方法


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機閱讀
- 01-10C#中split用法實例總結(jié)
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-11ajax實現(xiàn)頁面的局部加載
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 08-05織夢dedecms什么時候用欄目交叉功能?


