1.定义接口
[ServiceContract(SessionMode
=
SessionMode.Required,CallbackContract
=
typeof
(IWriteLogCallback))]
public
interface
ILogService
{
[OperationContract(IsInitiating
=
true
,IsTerminating
=
false
)]
void
Write(
string
logMsg);
[OperationContract(IsInitiating
=
true
,IsTerminating
=
false
)]
void
RegisterListener();
[OperationContract(IsInitiating
=
false
,IsTerminating
=
false
)]
void
UnregisterListener();
}
[ServiceContract]
public
interface
IWriteLogCallback
{
[OperationContract(IsOneWay
=
true
)]
void
OnWriteLog(
string
logMsg);
}
为了简单举了一个写日志的例子,Write(string logMsg)就是写入日志的方法,参数logMsg是需要写入的日志信息。当客户单没有调用RegisterListener()订阅事件的时候,是不会收到写日志的事件通知的,相应的要获得写日志的事件通知,就需要调用RegisterListener()方法。如果要取消订阅就调用UnregisterListener()方法。写日志的功能和事件的订阅功能是分开的。
2.服务实现
[ServiceBehavior(
IncludeExceptionDetailInFaults
=
true
,
InstanceContextMode
=
InstanceContextMode.Single,
ConcurrencyMode
=
ConcurrencyMode.Multiple)]
class
LogService:ILogService
{
public
LogService()
{
Trace.WriteLine(
"
CreateLogServiceInstance.
"
);
}
Dictionary
<
string
,OperationContext
>
listeners
=
new
Dictionary
<
string
,OperationContext
>
();
private
void
BroadCast(
string
logMsg)
{
List
<
string
>
errorClints
=
new
List
<
string
>
();
foreach
(KeyValuePair
<
string
,OperationContext
>
listener
in
listeners)
{
try
{
listener.Value.GetCallbackChannel
<
IWriteLogCallback
>
().OnWriteLog(logMsg);
}
catch
(System.Exceptione)
{
errorClints.Add(listener.Key);
Trace.WriteLine(
"
BROADEVENTERROR:
"
+
e.Message);
}
}
foreach
(
string
id
in
errorClints)
{
listeners.Remove(id);
}
}
#region
ILogService成员
public
void
Write(
string
logMsg)
{
Trace.WriteLine(
"
WriteLOG:
"
+
logMsg);
BroadCast(logMsg);
}
public
void
RegisterListener()
{
listeners.Add(OperationContext.Current.SessionId,OperationContext.Current);
Trace.WriteLine(
"
SessionID:
"
+
OperationContext.Current.SessionId);
Trace.WriteLine(
"
Registerlistener.ClientCount:
"
+
listeners.Count.ToString());
}
public
void
UnregisterListener()
{
listeners.Remove(OperationContext.Current.SessionId);
Trace.WriteLine(
"
SessionID:
"
+
OperationContext.Current.SessionId);
Trace.WriteLine(
"
Unregisterlistener.ClientCount:
"
+
listeners.Count.ToString());
}
#endregion
}
Dictionary<string, OperationContext> listeners包含了所有的事件订阅者。发布事件的时候,如果调用订阅者的回调函数失败,就把该订阅者从listeners移除。代码很简单,就不多说了。
3.客户端访问
定义回调的客户端:
class
LogClient:IWriteLogCallback
{
#region
IWriteLogCallback成员
public
void
OnWriteLog(
string
logMsg)
{
Trace.WriteLine(
"
RECVLOGEVENT:
"
+
logMsg);
}
#endregion
}
然后在程序中使用它:
class
Program
{
static
void
Main(
string
[]args)
{
Trace.Listeners.Add(
new
ConsoleTraceListener());
LogClientclient
=
new
LogClient();
ILogServiceservice
=
DuplexChannelFactory
<
ILogService
>
.CreateChannel(client,
new
WSDualHttpBinding(),
new
EndpointAddress(
"
http://localhost:8888/log
"
));
//
订阅消息
service.RegisterListener();
service.Write(
"
Clientstart
"
);
Console.WriteLine(
"
Pressenterkeytoexit.
"
);
Console.ReadLine();
service.UnregisterListener();
}
需要注意的问题:
A. 因为客户也要监听端口,所以确保防火墙没有对它进行阻止。
B. 这里使用的是单实例的服务,所以需要进行多进程访问的保护,才能实际使用。