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( " 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移除。代码很简单,就不多说了。 3.客户端访问 定义回调的客户端: class LogClient:IWriteLogCallback { #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(); } 需要注意的问题: A. 因为客户也要监听端口,所以确保防火墙没有对它进行阻止。 B. 这里使用的是单实例的服务,所以需要进行多进程访问的保护,才能实际使用。