測試Remoting服務端和客戶端的雙向通信
最近有個項目,大致需求是,服務端發送消息通知客戶端上傳指定的數據,然后處理后一部分顯示在服務端界面上。也是在網上胡亂搜索一片,看到一篇Remoting廣播事件的博客+Remoting覺得這么還可以做。
大致原理是:通過服務端廣播事件,客戶端通過調用遠程類將數據以參數的方式傳給服務端,然后激活服務端界面層的事件就達到雙向了。都是靠遠程類里的2個事件,一個給服務端,一個給客戶端,分別交叉執行。這就相當于: 服務端界面--遠程類--客戶端界面,遠程類起到了一個中間人的作用樣,是吧?
先看看服務端封裝的Remoting的類
{
HttpChannel tcpC;
public static RemotingObject.Remoter Obj = null;
static RemotingServer instance = new RemotingServer();
public static RemotingServer GetRemotingServer
{
get { return instance; }
}
RemotingServer()
{
//tcpC = new TcpChannel(9000);
}
public void ServerTakeOn()
{
//if (!ExistServer(tcpC.ChannelName))
//{
RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject.Remoter), "RemotingService", WellKnownObjectMode.Singleton);
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 9500;
tcpC = new HttpChannel(props, clientProvider, serverProvider);
ChannelServices.RegisterChannel(tcpC);
Obj = new RemotingObject.Remoter();
ObjRef objRef = RemotingServices.Marshal(Obj, "RemotingMessage.soap");
//}
}
public void ServerTakeOff()
{
if (ExistServer(tcpC.ChannelName))
{
tcpC.StopListening(null);
ChannelServices.UnregisterChannel(tcpC);
}
}
private bool ExistServer(string serverName)
{
bool tmp_b = false;
IChannel[] list = ChannelServices.RegisteredChannels;
foreach (IChannel ic in list)
{
HttpChannel tmp_tcp = (HttpChannel)ic;
if(tmp_tcp.ChannelName==serverName)
{
tmp_b = true;
break;
}
}
return tmp_b;
}
}
下面2句代碼特別的重要是服務端界面能和客戶端共同操作的遠程類
ObjRef objRef = RemotingServices.Marshal(Obj, "RemotingMessage.soap");
接下來看看客戶端的Remoting是怎么樣的
{
HttpChannel tcp;
IMessage imessage = null;
EventWrapper wrapper = null;
string _mes;
public string ErrorMes
{
get { return _mes; }
}
public IMessage GetObject1
{
get { return imessage; }
}
public EventWrapper GetObject2
{
get { return wrapper; }
}
static RemotingClient _client = new RemotingClient();
public static RemotingClient GetObject
{
get { return _client; }
}
RemotingClient()
{
}
public bool Connect(string serverIP)
{
try
{
//_object = (RemotingObject.Remoter)Activator.GetObject(typeof(RemotingObject.Remoter),
// string.Format("tcp://{0}:9000/RemotingService", serverIP));
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 0;
tcp = new HttpChannel(props, clientProvider, serverProvider);
ChannelServices.RegisterChannel(tcp);
//RemotingConfiguration.RegisterActivatedClientType(typeof(IMessage), string.Format("tcp://{0}:9000/RemotingService", serverIP));
imessage = (IMessage)Activator.GetObject(typeof(IMessage), string.Format("http://{0}:9500/RemotingMessage.soap", serverIP));
wrapper = new EventWrapper();
return true;
}
catch(Exception ex)
{
_mes = ex.Message;
return false;
}
}
public void UnConnect()
{
if (ExistServer(tcp.ChannelName))
{
ChannelServices.UnregisterChannel(tcp);
}
}
private bool ExistServer(string serverName)
{
bool tmp_b = false;
IChannel[] list = ChannelServices.RegisteredChannels;
foreach (IChannel ic in list)
{
HttpChannel tmp_tcp = (HttpChannel)ic;
if (tmp_tcp.ChannelName == serverName)
{
tmp_b = true;
break;
}
}
return tmp_b;
}
}
是通過HTTP協議的,如果是域名的話,好像要先解析成IP吧?連接服務端,獲取遠程對象,給出屬性返回讓客戶端界面能操作遠程類
接下來就是客戶端連接的代碼,連接成功后,取出遠程類,關聯遠程類的事件
private void btnConnect_Click(object sender, EventArgs e)
{
if (!client.Connect(tbxIp.Text))
{
MesBox.Show(client.ErrorMes, 0);
return;
}
imessage = client.GetObject1;
wrapper = client.GetObject2;
try
{
wrapper.SendMessageEven += new SendMessageEvenHandler(GetTxt);
imessage.SendMessageEven += new SendMessageEvenHandler(wrapper.SendMessageing);
}
catch (Exception ex)
{
MesBox.Show(ex.Message, 0);
return;
}
btnConnect.Text = "已連接";
btnConnect.Enabled = false;
}
#endregion
private void GetTxt(string txt)
{
if (txt == "A")
{
string ip = GetlocalIP();
List<string> tmp = GetHardInfo();
imessage.AddInfo(ip, tmp);
}
}
#endregion
上面代碼中參數TXT==A,那是我自己胡弄的一個標識,不重要,然后獲取客戶端的IP和硬盤信息,通過調用AddInfo方法傳給服務端原程類。
在遠程類中有事件處理的方法,在AddInfo方法內會調用,那么關聯服務端界面的方法就會被執行,數據也通過參數傳遞過去了
{
#region IMessage 成員
public event SendMessageEvenHandler SendMessageEven;
public List<string> HardDeskInfo = new List<string>();
public void SendMessage( string txt)
{
if (SendMessageEven != null)
{
SendMessageEvenHandler tmp_even = null;
foreach (Delegate dl in SendMessageEven.GetInvocationList())
{
try
{
tmp_even = (SendMessageEvenHandler)dl;
tmp_even(txt);
}
catch
{
SendMessageEven -= tmp_even;
}
}
}
}
public void AddInfo(string IP, List<string> hardinfo)
{
string tmp = string.Empty;
foreach (string s in hardinfo)
{
//獲取剩余大小
string tmp1 = s.Substring(s.LastIndexOf("@") + 1);
//獲取分區@總大小
string tmp2 = s.Substring(0, s.Length - tmp1.Length - 1);
//獲取總大小
string tmp3 = tmp2.Substring(tmp2.LastIndexOf("@") + 1);
//獲取分區
string tmp4 = tmp2.Substring(0, tmp2.Length - tmp3.Length - 1);
//格式是 IP@分區@總大小@剩余大小
tmp = string.Format("{0}@{1}@{2}@{3}", IP, tmp4, tmp3, tmp1);
HardDeskInfo.Add(tmp);
ClientReciveData(tmp);
}
}
public void ClearInfo()
{
HardDeskInfo.Clear();
}
public override object InitializeLifetimeService()
{
return null;
}
#endregion
#region 激活服務端事件
public delegate void ClientReciveDataEvenHandler(string txt);
public event ClientReciveDataEvenHandler ClientReciveDataEven;
public void ClientReciveData(string txt)
{
if (ClientReciveDataEven != null)
{
ClientReciveDataEvenHandler tmp_even = null;
foreach (Delegate dl in ClientReciveDataEven.GetInvocationList())
{
try
{
tmp_even = (ClientReciveDataEvenHandler)dl;
tmp_even(txt);
}
catch
{
ClientReciveDataEven -= tmp_even;
}
}
}
}
#endregion
}
上面這個就是原創類了,是繼承于接口IMessage
{
public delegate void SendMessageEvenHandler(string txtt);
public interface IMessage
{
event SendMessageEvenHandler SendMessageEven;
void SendMessage(string txt);
void AddInfo(string IP, List<string> hardinfo);
}
}
接口和遠程類是不同的類庫生成的DLL,在服務端2個都會被調用,但是客戶端只會調用接口的DLL,中間還有一個事件適配器吧?是這么叫的吧?這個方式源于Remoting服務端事件廣播
{
public class EventWrapper : MarshalByRefObject
{
public event SendMessageEvenHandler SendMessageEven;
public void SendMessageing( string txt)
{
SendMessageEven(txt);
}
public override object InitializeLifetimeService()
{
return null;
}
}
}
這2個的關系我到現在還沒搞懂的,不過能使用它們就很不錯啦
最后看看服務端界面方法關聯的遠程類事件
private void frmMain_Load(object sender, EventArgs e)
{
_server = RemotingServer.GetRemotingServer;
_server.ServerTakeOn();
RemotingServer.Obj.ClientReciveDataEven += new RemotingObject.Remoter.ClientReciveDataEvenHandler(ClientReciveData);
}
#endregion
#region 窗體關閉
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
RemotingServer.Obj.ClientReciveDataEven -= new RemotingObject.Remoter.ClientReciveDataEvenHandler(ClientReciveData);
_server.ServerTakeOff();
}
#endregion
#region 遠程類事件
private void ClientReciveData(string txt)
{
SetLst(txt);
}
#endregion
#region 設置列表
private void SetLst(string tmp)
{
string ip = string.Empty;
string dir = string.Empty;
string all = string.Empty;
string free = string.Empty;
//剩余大小
string tmp1 = tmp.Substring(tmp.LastIndexOf("@") + 1);
//IP@分區@總大小
string tmp2 = tmp.Substring(0, tmp.Length - tmp1.Length - 1);
//總大小
string tmp3 = tmp2.Substring(tmp2.LastIndexOf("@") + 1);
//IP@分區
string tmp4 = tmp2.Substring(0, tmp2.Length - tmp3.Length - 1);
//分區
string tmp5 = tmp4.Substring(tmp4.LastIndexOf("@") + 1);
//IP
string tmp6 = tmp4.Substring(0, tmp4.Length - tmp5.Length - 1);
ip = tmp6;
dir = tmp5;
all = tmp3;
free = tmp1;
ListViewItem itm = new ListViewItem();
itm.SubItems[0].Text = ip;
itm.SubItems.Add(dir);
itm.SubItems.Add(all);
itm.SubItems.Add(free);
lst.Items.Add(itm);
}
接受到參數傳遞的數據,處理下放到ListView顯示出來,不過我記得Remoting遠程類所激活的事件都應該是子線程的吧?怎么我這沒報錯(子線程不能操作主線程的控件)呢?
整個工程大概就是這樣的,希望上面的思路不是很亂吧?

浙公網安備 33010602011771號