最近有个项目,大致需求是,服务端发送消息通知客户端上传指定的数据,然后处理后一部分显示在服务端界面上。也是在网上胡乱搜索一片,看到一篇Remoting广播事件的博客+Remoting觉得这么还可以做。
大致原理是:通过服务端广播事件,客户端通过调用远程类将数据以参数的方式传给服务端,然后激活服务端界面层的事件就达到双向了。都是靠远程类里的2个事件,一个给服务端,一个给客户端,分别交叉执行。这就相当于: 服务端界面--远程类--客户端界面,远程类起到了一个中间人的作用样,是吧?
先看看服务端封装的Remoting的类
public
sealed
class
RemotingServer
{
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句代码特别的重要是服务端界面能和客户端共同操作的远程类
Obj
=
new
RemotingObject.Remoter();
ObjRef objRef
=
RemotingServices.Marshal(Obj,
"
RemotingMessage.soap
"
);
接下来看看客户端的Remoting是怎么样的
public
sealed
class
RemotingClient
{
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吧?连接服务端,获取远程对象,给出属性返回让客户端界面能操作远程类
接下来就是客户端连接的代码,连接成功后,取出远程类,关联远程类的事件
#region
连接
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
#region
服务端广播
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方法内会调用,那么关联服务端界面的方法就会被执行,数据也通过参数传递过去了
public
class
Remoter : MarshalByRefObject, IMessage
{
#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
namespace
RemotingObject.Common
{
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服务端事件广播
namespace
RemotingObject.Common
{
public
class
EventWrapper : MarshalByRefObject
{
public
event
SendMessageEvenHandler SendMessageEven;
public
void
SendMessageing(
string
txt)
{
SendMessageEven(txt);
}
public
override
object
InitializeLifetimeService()
{
return
null
;
}
}
}
这2个的关系我到现在还没搞懂的,不过能使用它们就很不错啦
最后看看服务端界面方法关联的远程类事件
#region
窗体加载
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远程类所激活的事件都应该是子线程的吧?怎么我这没报错(子线程不能操作主线程的控件)呢?
整个工程大概就是这样的,希望上面的思路不是很乱吧?