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);
}
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( " Create LogService Instance. " );
}
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.Exception e)
{
errorClints.Add(listener.Key);
Trace.WriteLine( " BROAD EVENT ERROR: " + e.Message);
}
}
foreach ( string id in errorClints)
{
listeners.Remove(id);
}
}
#region ILogService 成员
public void Write( string logMsg)
{
Trace.WriteLine( " Write LOG: " + logMsg);
BroadCast(logMsg);
}
public void RegisterListener()
{
listeners.Add(OperationContext.Current.SessionId, OperationContext.Current);
Trace.WriteLine( " SessionID: " + OperationContext.Current.SessionId);
Trace.WriteLine( " Register listener. Client Count: " + listeners.Count.ToString());
}
public void UnregisterListener()
{
listeners.Remove(OperationContext.Current.SessionId);
Trace.WriteLine( " SessionID: " + OperationContext.Current.SessionId);
Trace.WriteLine( " Unregister listener. Client Count: " + listeners.Count.ToString());
}
#endregion
}
Dictionary<string, OperationContext> listeners包含了所有的事件订阅者。发布事件的时候,如果调用订阅者的回调函数失败,就把该订阅者从listeners移除。代码很简单,就不多说了。IncludeExceptionDetailInFaults = true ,
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
class LogService:ILogService
{
public LogService()
{
Trace.WriteLine( " Create LogService Instance. " );
}
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.Exception e)
{
errorClints.Add(listener.Key);
Trace.WriteLine( " BROAD EVENT ERROR: " + e.Message);
}
}
foreach ( string id in errorClints)
{
listeners.Remove(id);
}
}
#region ILogService 成员
public void Write( string logMsg)
{
Trace.WriteLine( " Write LOG: " + logMsg);
BroadCast(logMsg);
}
public void RegisterListener()
{
listeners.Add(OperationContext.Current.SessionId, OperationContext.Current);
Trace.WriteLine( " SessionID: " + OperationContext.Current.SessionId);
Trace.WriteLine( " Register listener. Client Count: " + listeners.Count.ToString());
}
public void UnregisterListener()
{
listeners.Remove(OperationContext.Current.SessionId);
Trace.WriteLine( " SessionID: " + OperationContext.Current.SessionId);
Trace.WriteLine( " Unregister listener. Client Count: " + listeners.Count.ToString());
}
#endregion
}
3.客户端访问
定义回调的客户端:
class
LogClient:IWriteLogCallback
{
#region IWriteLogCallback 成员
public void OnWriteLog( string logMsg)
{
Trace.WriteLine( " RECV LOG EVENT: " + logMsg);
}
#endregion
}
{
#region IWriteLogCallback 成员
public void OnWriteLog( string logMsg)
{
Trace.WriteLine( " RECV LOG EVENT: " + logMsg);
}
#endregion
}
然后在程序中使用它:
class
Program
{
static void Main( string [] args)
{
Trace.Listeners.Add( new ConsoleTraceListener());
LogClient client = new LogClient();
ILogService service = DuplexChannelFactory < ILogService > .CreateChannel(client,
new WSDualHttpBinding(), new EndpointAddress( " http://localhost:8888/log " ));
// 订阅消息
service.RegisterListener();
service.Write( " Client start " );
Console.WriteLine( " Press enter key to exit. " );
Console.ReadLine();
service.UnregisterListener();
}
{
static void Main( string [] args)
{
Trace.Listeners.Add( new ConsoleTraceListener());
LogClient client = new LogClient();
ILogService service = DuplexChannelFactory < ILogService > .CreateChannel(client,
new WSDualHttpBinding(), new EndpointAddress( " http://localhost:8888/log " ));
// 订阅消息
service.RegisterListener();
service.Write( " Client start " );
Console.WriteLine( " Press enter key to exit. " );
Console.ReadLine();
service.UnregisterListener();
}
需要注意的问题:
A. 因为客户也要监听端口,所以确保防火墙没有对它进行阻止。
B. 这里使用的是单实例的服务,所以需要进行多进程访问的保护,才能实际使用。