2010-08-02 29 views
10

Trong một nỗ lực để trau dồi một số dịch vụ ví dụ được sử dụng làm tài liệu tham khảo cho các kịch bản nội bộ của chúng tôi, tôi đã tạo ví dụ WCF Duplex Channel này.WCF Duplex Callback Sample thất bại

Phần song công không hoạt động và tôi hy vọng tất cả chúng ta có thể cùng nhau tìm ra. Tôi ghét đăng nhiều mã này, nhưng tôi cảm thấy tôi đã cắt ngắn xuống như WCF có thể đi, trong khi kết hợp tất cả các phần tôi hy vọng sẽ được cộng đồng xem xét. Có thể có một số ý tưởng thực sự tồi tệ ở đây, tôi không nói nó đúng, nó chỉ là những gì tôi đã có cho đến nay.

Có ba phần. Kênh, Máy chủ và Ứng dụng khách. Ba dự án, và ở đây, ba tệp mã. Không có cấu hình XML, mọi thứ được mã hóa. Theo sau là đầu ra mã.

Channel.proj/Channel.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ServiceModel; 

namespace Channel 
{ 
    public interface IDuplexSyncCallback 
    { 
     [OperationContract] 
     string CallbackSync(string message, DateTimeOffset timestamp); 
    } 

    [ServiceContract(CallbackContract = typeof(IDuplexSyncCallback))] 
    public interface IDuplexSyncContract 
    { 
     [OperationContract] 
     void Ping(); 

     [OperationContract] 
     void Enroll(); 

     [OperationContract] 
     void Unenroll(); 
    } 
} 

Server.proj/Server.cs, tài liệu tham khảo Kênh, System.Runtime.Serialization, System.ServiceModel

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ServiceModel; 
using System.Timers; 
using Channel; 
using System.Diagnostics; 
using System.Net.Security; 

namespace Server 
{ 
    class Program 
    { 
     // All of this just starts up the service with these hardcoded configurations 
     static void Main(string[] args) 
     { 
      ServiceImplementation implementation = new ServiceImplementation(); 
      ServiceHost service = new ServiceHost(implementation); 

      NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport); 
      binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; 
      binding.Security.Mode = SecurityMode.Transport; 
      binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows; 
      binding.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign; 
      binding.ListenBacklog = 1000; 
      binding.MaxConnections = 30; 
      binding.MaxReceivedMessageSize = 2147483647; 
      binding.ReaderQuotas.MaxStringContentLength = 2147483647; 
      binding.ReaderQuotas.MaxArrayLength = 2147483647; 
      binding.SendTimeout = TimeSpan.FromSeconds(2); 
      binding.ReceiveTimeout = TimeSpan.FromSeconds(10 * 60); // 10 minutes is the default if not specified 
      binding.ReliableSession.Enabled = true; 
      binding.ReliableSession.Ordered = true; 

      service.AddServiceEndpoint(typeof(IDuplexSyncContract), binding, new Uri("net.tcp://localhost:3828")); 

      service.Open(); 

      Console.WriteLine("Server Running ... Press any key to quit"); 
      Console.ReadKey(true); 

      service.Abort(); 
      service.Close(); 
      implementation = null; 
      service = null; 
     } 
    } 

    /// <summary> 
    /// ServiceImplementation of IDuplexSyncContract 
    /// </summary> 
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
     MaxItemsInObjectGraph = 2147483647, 
     IncludeExceptionDetailInFaults = true, 
     ConcurrencyMode = ConcurrencyMode.Multiple, 
     UseSynchronizationContext = false)] 
    class ServiceImplementation : IDuplexSyncContract 
    { 
     Timer announcementTimer = new Timer(5000); // Every 5 seconds 
     int messageNumber = 0; // message number incrementer - not threadsafe, just for debugging. 

     public ServiceImplementation() 
     { 
      announcementTimer.Elapsed += new ElapsedEventHandler(announcementTimer_Elapsed); 
      announcementTimer.AutoReset = true; 
      announcementTimer.Enabled = true; 
     } 

     void announcementTimer_Elapsed(object sender, ElapsedEventArgs e) 
     { 
      AnnounceSync(string.Format("HELLO? (#{0})", messageNumber++)); 
     } 

     #region IDuplexSyncContract Members 
     List<IDuplexSyncCallback> syncCallbacks = new List<IDuplexSyncCallback>(); 

     /// <summary> 
     /// Simple Ping liveness 
     /// </summary> 
     [OperationBehavior] 
     public void Ping() { return; } 

     /// <summary> 
     /// Add channel to subscribers 
     /// </summary> 
     [OperationBehavior] 
     void IDuplexSyncContract.Enroll() 
     { 
      IDuplexSyncCallback current = System.ServiceModel.OperationContext.Current.GetCallbackChannel<IDuplexSyncCallback>(); 

      lock (syncCallbacks) 
      { 
       syncCallbacks.Add(current); 

       Trace.WriteLine("Enrollment Complete"); 
      } 
     } 

     /// <summary> 
     /// Remove channel from subscribers 
     /// </summary> 
     [OperationBehavior] 
     void IDuplexSyncContract.Unenroll() 
     { 
      IDuplexSyncCallback current = System.ServiceModel.OperationContext.Current.GetCallbackChannel<IDuplexSyncCallback>(); 

      lock (syncCallbacks) 
      { 
       syncCallbacks.Remove(current); 

       Trace.WriteLine("Unenrollment Complete"); 
      } 
     } 

     /// <summary> 
     /// Callback to clients over enrolled channels 
     /// </summary> 
     /// <param name="message"></param> 
     void AnnounceSync(string message) 
     { 
      var now = DateTimeOffset.Now; 

      if (message.Length > 2000) message = message.Substring(0, 2000 - "[TRUNCATED]".Length) + "[TRUNCATED]"; 
      Trace.WriteLine(string.Format("{0}: {1}", now.ToString("mm:ss.fff"), message)); 

      lock (syncCallbacks) 
      { 
       foreach (var callback in syncCallbacks.ToArray()) 
       { 
        Console.WriteLine("Sending \"{0}\" synchronously ...", message); 

        CommunicationState state = ((ICommunicationObject)callback).State; 

        switch (state) 
        { 
         case CommunicationState.Opened: 
          try 
          { 
           Console.WriteLine("Client said '{0}'", callback.CallbackSync(message, now)); 
          } 
          catch (Exception ex) 
          { 
           // Timeout Error happens here 
           syncCallbacks.Remove(callback); 
           Console.WriteLine("Removed client"); 
          } 
          break; 
         case CommunicationState.Created: 
         case CommunicationState.Opening: 
          break; 
         case CommunicationState.Faulted: 
         case CommunicationState.Closed: 
         case CommunicationState.Closing: 
         default: 
          syncCallbacks.Remove(callback); 
          Console.WriteLine("Removed client"); 
          break; 
        } 
       } 
      } 
     } 
     #endregion 
    } 
} 

Client.proj/Client .cs, tham chiếu Kênh, Hệ thống.Runtime.Serialization, System.ServiceModel

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ServiceModel; 
using System.Timers; 
using System.Diagnostics; 
using Channel; 
using System.Net; 

namespace Client 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var callbackSyncProxy = new CallbackSyncProxy(new Uri("net.tcp://localhost:3828"), CredentialCache.DefaultNetworkCredentials)) 
      { 
       callbackSyncProxy.Faulted += (s, e) => Console.WriteLine("CallbackSyncProxy Faulted."); 
       callbackSyncProxy.ConnectionUnavailable += (s, e) => Console.WriteLine("CallbackSyncProxy ConnectionUnavailable."); 
       callbackSyncProxy.ConnectionRecovered += (s, e) => Console.WriteLine("CallbackSyncProxy ConnectionRecovered."); 

       callbackSyncProxy.Ping(); 
       callbackSyncProxy.Ping(); 
       callbackSyncProxy.Ping(); 

       Console.WriteLine("Pings completed. Enrolling ..."); 

       callbackSyncProxy.AnnouncementSyncHandler = AnnouncementHandler; 

       Console.WriteLine("Enrolled and waiting. Press any key to quit ..."); 
       Console.ReadKey(true); // Wait for quit 
      } 
     } 

     /// <summary> 
     /// Called by the server through DuplexChannel 
     /// </summary> 
     /// <param name="message"></param> 
     /// <param name="timeStamp"></param> 
     /// <returns></returns> 
     static string AnnouncementHandler(string message, DateTimeOffset timeStamp) 
     { 
      Console.WriteLine("{0}: {1}", timeStamp, message); 

      return string.Format("Dear Server, thanks for that message at {0}.", timeStamp); 
     } 
    } 

    /// <summary> 
    /// Encapsulates the client-side WCF setup logic. 
    /// 
    /// There are 3 events Faulted, ConnectionUnavailable, ConnectionRecovered that might be of interest to the consumer 
    /// Enroll and Unenroll of the ServiceContract are called when setting an AnnouncementSyncHandler 
    /// Ping, when set correctly against the server's send/receive timeouts, will keep the connection alive 
    /// </summary> 
    public class CallbackSyncProxy : IDisposable 
    { 
     Uri listen; 
     NetworkCredential credentials; 
     NetTcpBinding binding; 
     EndpointAddress serverEndpoint; 
     ChannelFactory<IDuplexSyncContract> channelFactory; 
     DisposableChannel<IDuplexSyncContract> channel; 

     readonly DuplexSyncCallback callback = new DuplexSyncCallback(); 

     object sync = new object(); 
     bool enrolled; 
     Timer pingTimer = new Timer(); 
     bool quit = false; // set during dispose 

     // Events of interest to consumer 
     public event EventHandler Faulted; 
     public event EventHandler ConnectionUnavailable; 
     public event EventHandler ConnectionRecovered; 

     // AnnouncementSyncHandler property. When set to non-null delegate, Enrolls client with server. 
     // passes through to the DuplexSyncCallback callback.AnnouncementSyncHandler 
     public Func<string, DateTimeOffset, string> AnnouncementSyncHandler 
     { 
      get 
      { 
       Func<string, DateTimeOffset, string> temp = null; 

       lock (sync) 
       { 
        temp = callback.AnnouncementSyncHandler; 
       } 
       return temp; 
      } 
      set 
      { 
       lock (sync) 
       { 
        if (callback.AnnouncementSyncHandler == null && value != null) 
        { 
         callback.AnnouncementSyncHandler = value; 

         Enroll(); 
        } 
        else if (callback.AnnouncementSyncHandler != null && value == null) 
        { 
         Unenroll(); 

         callback.AnnouncementSyncHandler = null; 
        } 
        else // null to null or function to function, just update it 
        { 
         callback.AnnouncementSyncHandler = value; 
        } 
       } 
      } 
     } 

     /// <summary> 
     /// using (var proxy = new CallbackSyncProxy(listen, CredentialCache.DefaultNetworkCredentials) { ... } 
     /// </summary> 
     public CallbackSyncProxy(Uri listen, NetworkCredential credentials) 
     { 
      this.listen = listen; 
      this.credentials = credentials; 

      binding = new NetTcpBinding(SecurityMode.Transport); 
      binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; 
      binding.Security.Mode = SecurityMode.Transport; 
      binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows; 
      binding.MaxReceivedMessageSize = 2147483647; 
      binding.ReaderQuotas.MaxArrayLength = 2147483647; 
      binding.ReaderQuotas.MaxBytesPerRead = 2147483647; 
      binding.ReaderQuotas.MaxDepth = 2147483647; 
      binding.ReaderQuotas.MaxStringContentLength = 2147483647; 
      binding.ReliableSession.Enabled = true; 
      binding.ReliableSession.Ordered = true; 
      serverEndpoint = new EndpointAddress(listen); 

      pingTimer.AutoReset = true; 
      pingTimer.Elapsed += pingTimer_Elapsed; 
      pingTimer.Interval = 20000; 
     } 

     /// <summary> 
     /// Keep the connection alive by pinging at some set minimum interval 
     /// </summary> 
     void pingTimer_Elapsed(object sender, ElapsedEventArgs e) 
     { 
      bool locked = false; 

      try 
      { 
       locked = System.Threading.Monitor.TryEnter(sync, 100); 
       if (!locked) 
       { 
        Console.WriteLine("Unable to ping because synchronization lock could not be aquired in a timely fashion"); 
        return; 
       } 
       Debug.Assert(channel != null, "CallbackSyncProxy.channel is unexpectedly null"); 

       try 
       { 
        channel.Service.Ping(); 
       } 
       catch 
       { 
        Console.WriteLine("Unable to ping"); 
       } 
      } 
      finally 
      { 
       if (locked) System.Threading.Monitor.Exit(sync); 
      } 
     } 

     /// <summary> 
     /// Ping is a keep-alive, but can also be called by the consuming code 
     /// </summary> 
     public void Ping() 
     { 
      lock (sync) 
      { 
       if (channel != null) 
       { 
        channel.Service.Ping(); 
       } 
       else 
       { 
        using (var c = new DisposableChannel<IDuplexSyncContract>(GetChannelFactory().CreateChannel())) 
        { 
         c.Service.Ping(); 
        } 
       } 
      } 
     } 

     /// <summary> 
     /// Enrollment - called when AnnouncementSyncHandler is assigned 
     /// </summary> 
     void Enroll() 
     { 
      lock (sync) 
      { 
       if (!enrolled) 
       { 
        Debug.Assert(channel == null, "CallbackSyncProxy.channel is unexpectedly not null"); 

        var c = new DisposableChannel<IDuplexSyncContract>(GetChannelFactory().CreateChannel()); 

        ((ICommunicationObject)c.Service).Open(); 

        ((ICommunicationObject)c.Service).Faulted += new EventHandler(CallbackChannel_Faulted); 

        c.Service.Enroll(); 

        channel = c; 

        Debug.Assert(!pingTimer.Enabled, "CallbackSyncProxy.pingTimer unexpectedly Enabled"); 

        pingTimer.Start(); 

        enrolled = true; 
       } 
      } 
     } 

     /// <summary> 
     /// Unenrollment - called when AnnouncementSyncHandler is set to null 
     /// </summary> 
     void Unenroll() 
     { 
      lock (sync) 
      { 
       if (callback.AnnouncementSyncHandler != null) 
       { 
        Debug.Assert(channel != null, "CallbackSyncProxy.channel is unexpectedly null"); 

        channel.Service.Unenroll(); 

        Debug.Assert(!pingTimer.Enabled, "CallbackSyncProxy.pingTimer unexpectedly Disabled"); 

        pingTimer.Stop(); 

        enrolled = false; 
       } 
      } 
     } 

     /// <summary> 
     /// Used during enrollment to establish a channel. 
     /// </summary> 
     /// <returns></returns> 
     ChannelFactory<IDuplexSyncContract> GetChannelFactory() 
     { 
      lock (sync) 
      { 
       if (channelFactory != null && 
        channelFactory.State != CommunicationState.Opened) 
       { 
        ResetChannel(); 
       } 

       if (channelFactory == null) 
       { 
        channelFactory = new DuplexChannelFactory<IDuplexSyncContract>(callback, binding, serverEndpoint); 

        channelFactory.Credentials.Windows.ClientCredential = credentials; 

        foreach (var op in channelFactory.Endpoint.Contract.Operations) 
        { 
         var b = op.Behaviors[typeof(System.ServiceModel.Description.DataContractSerializerOperationBehavior)] as System.ServiceModel.Description.DataContractSerializerOperationBehavior; 

         if (b != null) 
          b.MaxItemsInObjectGraph = 2147483647; 
        } 
       } 
      } 

      return channelFactory; 
     } 

     /// <summary> 
     /// Channel Fault handler, set during Enrollment 
     /// </summary> 
     void CallbackChannel_Faulted(object sender, EventArgs e) 
     { 
      lock (sync) 
      { 
       if (Faulted != null) 
       { 
        Faulted(this, new EventArgs()); 
       } 

       ResetChannel(); 

       pingTimer.Stop(); 
       enrolled = false; 

       if (callback.AnnouncementSyncHandler != null) 
       { 
        while (!quit) // set during Dispose 
        { 
         System.Threading.Thread.Sleep(500); 

         try 
         { 
          Enroll(); 

          if (ConnectionRecovered != null) 
          { 
           ConnectionRecovered(this, new EventArgs()); 

           break; 
          } 
         } 
         catch 
         { 
          if (ConnectionUnavailable != null) 
          { 
           ConnectionUnavailable(this, new EventArgs()); 
          } 
         } 
        } 
       } 
      } 
     } 

     /// <summary> 
     /// Reset the Channel & ChannelFactory if they are faulted and during dispose 
     /// </summary> 
     void ResetChannel() 
     { 
      lock (sync) 
      { 
       if (channel != null) 
       { 
        channel.Dispose(); 
        channel = null; 
       } 

       if (channelFactory != null) 
       { 
        if (channelFactory.State == CommunicationState.Faulted) 
         channelFactory.Abort(); 
        else 
         try 
         { 
          channelFactory.Close(); 
         } 
         catch 
         { 
          channelFactory.Abort(); 
         } 

        channelFactory = null; 
       } 
      } 
     } 

     // Disposing of me implies disposing of disposable members 
     #region IDisposable Members 
     bool disposed; 
     void IDisposable.Dispose() 
     { 
      if (!disposed) 
      { 
       Dispose(true); 
      } 

      GC.SuppressFinalize(this); 
     } 

     void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       quit = true; 

       ResetChannel(); 

       pingTimer.Stop(); 

       enrolled = false; 

       callback.AnnouncementSyncHandler = null; 
      } 

      disposed = true; 
     } 
     #endregion 
    } 

    /// <summary> 
    /// IDuplexSyncCallback implementation, instantiated through the CallbackSyncProxy 
    /// </summary> 
    [CallbackBehavior(UseSynchronizationContext = false, 
    ConcurrencyMode = ConcurrencyMode.Multiple, 
    IncludeExceptionDetailInFaults = true)] 
    class DuplexSyncCallback : IDuplexSyncCallback 
    { 
     // Passthrough handler delegates from the CallbackSyncProxy 
     #region AnnouncementSyncHandler passthrough property 
     Func<string, DateTimeOffset, string> announcementSyncHandler; 
     public Func<string, DateTimeOffset, string> AnnouncementSyncHandler 
     { 
      get 
      { 
       return announcementSyncHandler; 
      } 
      set 
      { 
       announcementSyncHandler = value; 
      } 
     } 
     #endregion 

     /// <summary> 
     /// IDuplexSyncCallback.CallbackSync 
     /// </summary> 
     [OperationBehavior] 
     public string CallbackSync(string message, DateTimeOffset timestamp) 
     { 
      if (announcementSyncHandler != null) 
      { 
       return announcementSyncHandler(message, timestamp); 
      } 
      else 
      { 
       return "Sorry, nobody was home"; 
      } 
     } 
    } 

    // This class wraps an ICommunicationObject so that it can be either Closed or Aborted properly with a using statement 
    // This was chosen over alternatives of elaborate try-catch-finally blocks in every calling method, or implementing a 
    // new Channel type that overrides Disposable with similar new behavior 
    sealed class DisposableChannel<T> : IDisposable 
    { 
     T proxy; 
     bool disposed; 

     public DisposableChannel(T proxy) 
     { 
      if (!(proxy is ICommunicationObject)) throw new ArgumentException("object of type ICommunicationObject expected", "proxy"); 

      this.proxy = proxy; 
     } 

     public T Service 
     { 
      get 
      { 
       if (disposed) throw new ObjectDisposedException("DisposableProxy"); 

       return proxy; 
      } 
     } 

     public void Dispose() 
     { 
      if (!disposed) 
      { 
       Dispose(true); 
      } 

      GC.SuppressFinalize(this); 
     } 

     void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       if (proxy != null) 
       { 
        ICommunicationObject ico = null; 

        if (proxy is ICommunicationObject) 
         ico = (ICommunicationObject)proxy; 

        // This state may change after the test and there's no known way to synchronize 
        // so that's why we just give it our best shot 
        if (ico.State == CommunicationState.Faulted) 
         ico.Abort(); // Known to be faulted 
        else 
         try 
         { 
          ico.Close(); // Attempt to close, this is the nice way and we ought to be nice 
         } 
         catch 
         { 
          ico.Abort(); // Sometimes being nice isn't an option 
         } 

        proxy = default(T); 
       } 
      } 

      disposed = true; 
     } 
    } 
} 

Đồng bộ hóa Outpu t:

>> Server Running ... Press any key to quit 
          Pings completed. Enrolling ... << 
      Enrolled and waiting. Press any key to quit ... << 
>> Sending "HELLO? (#0)" synchronously ... 
           CallbackSyncProxy Faulted. << 
        CallbackSyncProxy ConnectionRecovered. << 
>> Removed client 
>> Sending "HELLO? (#2)" synchronously ... 
        8/2/2010 2:47:32 PM -07:00: HELLO? (#2) << 
>> Removed client 

Như Andrew đã chỉ ra, vấn đề không hiển nhiên như vậy. "Sản lượng đối chiếu" này không phải là đầu ra mong muốn. Thay vào đó, tôi muốn máy chủ được chạy, Ping và đăng ký thành công, và sau đó cứ sau 5 giây, máy chủ sẽ "Gửi" HELLO? (#m) "đồng bộ" và ngay lập tức Máy khách sẽ chuyển đổi và trả về và Máy chủ sẽ nhận và in ra. Thay vào đó, các ping hoạt động, nhưng các lỗi Callback trong lần thử đầu tiên, được gửi tới máy khách trên kết nối lại nhưng không quay trở lại máy chủ và mọi thứ đều bị ngắt kết nối.

Các ngoại lệ duy nhất tôi thấy liên quan đến kênh đã bị lỗi trước đó và do đó không sử dụng được, nhưng chưa có lỗi nào thực sự khiến kênh tiếp cận trạng thái đó.

Tôi đã sử dụng mã tương tự với [OperationalBehavior(IsOneWay= true)] nhiều lần. Lạ lùng là trường hợp này dường như phổ biến hơn là khiến tôi đau buồn như vậy.

Ngoại lệ bị bắt ở phía máy chủ mà tôi không hiểu, là:
System.TimeoutException: "Thao tác yêu cầu này được gửi tới schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous không Thời gian được phân bổ cho hoạt động này có thể là một phần của thời gian chờ lâu hơn. Điều này có thể do dịch vụ vẫn đang xử lý thao tác hoặc vì dịch vụ không thể gửi Vui lòng xem xét tăng thời gian chờ hoạt động (bằng cách truyền kênh/proxy tới IContextChannel và thiết lập thuộc tính OperationTimeout) và đảm bảo rằng dịch vụ có thể kết nối với máy khách. "

+1

Bạn nên nói CÁCH phần song công "không hoạt động". Nếu không, không có động lực cho bất kỳ ai đọc mã. –

+0

Điểm tốt. Tôi đã thêm nhận xét ở dưới cùng. Điều đó có tốt hơn không? –

+0

Có lẽ bạn nên đặt dự án có thể xây dựng và chạy được trong một tệp zip ở đâu đó trên mạng để ý tưởng bất cứ ai có thời gian có thể xây dựng, xcopy triển khai và chạy trong 5 phút và sau đó xem trong trình gỡ lỗi những gì đang xảy ra và có sự đảm bảo VS để đào sâu vào mã. BTW StackOverfkow cần thay đổi CSS của họ để bắt đầu sử dụng khoảng cách chặt chẽ cho mã :-) – ZXX

Trả lời

1

Rất ngớ ngẩn/trầm trọng hơn, nhưng có vẻ như là ProtectionLevel.EncryptAndSign là sự cố. Tôi tìm thấy thông báo lỗi trên Google không thường xuyên liên quan đến các ràng buộc và xác thực Windows. Dẫn tôi đoán rằng có lẽ giao tiếp ngược dòng không hoạt động do một cái gì đó liên quan đến mã hóa ràng buộc ... hay cái gì đó. Nhưng đặt nó vào ProtectionLevel.None thay vì đột nhiên cho phép kênh song công hoạt động cho các phương thức hai chiều (các phương thức trả về các giá trị trở lại máy chủ)

Tôi không nói rằng tắt mức bảo vệ là một ý tưởng hay, nhưng ít nhất nó là một dẫn đáng kể. Nếu bạn cần những lợi ích của EncryptAndSign, bạn có thể điều tra thêm từ đó.

1

Điều này có thể không khắc phục được hoàn toàn sự cố của bạn nhưng nhìn vào mã của bạn, IDuplexSyncCallback chắc chắn là một sự nghi ngờ.Một phần của việc triển khai dịch vụ được thực hiện nhưng cũng cần được trang trí với ServiceContractAttribute. Khi thực hiện cuộc gọi lại, nó cũng phải được chỉ định là một chiều. Dưới đây là một ví dụ về những gì tôi đã làm trong quá khứ cho một hợp đồng gọi lại và cũng có thể giúp bạn.

[ServiceContract] 
public interface IDuplexSyncCallback 
{ 
    [OperationContract(IsOneWay = true) 
    string CallbackSync(string message, DateTimeOffset timestamp); 
} 
+0

Ngay cả trong các mẫu được tham chiếu bởi larsw, giao diện Gọi lại không được đánh dấu bằng [ServiceContract]. Tôi đã đọc ở nơi khác mà nó không phải là cần thiết, nhưng tôi không thể tìm thấy url đó một lần nữa. Trong mọi trường hợp, tôi đã thử nó chỉ để không cứng đầu và nó không có sự khác biệt. Vấn đề thứ hai của IsOneWay = true là không thích hợp cho kịch bản này - tôi cần một câu trả lời! Tôi đã không đọc rằng IsOneWay = true được yêu cầu trên Callbacks (mà họ không thể trả lại một giá trị) –

+0

OneWay là không cần thiết, đó là chính xác. Callbacks nói chung là không đồng bộ và đó là lý do tại sao bạn có thể tìm thấy các ví dụ theo cách đó. Xin lỗi vì đã không giúp được gì. – jlafay

+0

Cảm ơn bạn đã xem qua. –

3

Trên một máy chủ trong phương pháp AnnounceSync thêm FaultException xử lý và bạn sẽ được thông báo rằng không có phản hồi từ máy chủ (mà trong trường hợp của bạn là một khách hàng) có nghĩa là không có gọi lại nhận được.

Như bạn đã đề xuất do hết thời gian chờ. Vì vậy, hãy thay đổi

binding.SendTimeout = TimeSpan.FromSeconds(3); 

Nó sẽ hoạt động như mong đợi.

try 
{ 
    Console.WriteLine("Client said '{0}'",callback.CallbackSync(message, now)); 
} 
catch (FaultException fex) 
{ 
    syncCallbacks.Remove(callback); 
    Console.WriteLine("Failed to call Client because" + fex.Reason); 
    Console.WriteLine(fex.Message); 
} 
+1

Sự tự tin của bạn là lôi cuốn, nhưng tôi không thể làm được điều này. Tôi đã thêm khối catch này vào server.cs và tôi đã thay đổi thời gian chờ từ 2 giây xuống 3. Không có thay đổi về hành vi. Ngoại lệ được ném là một TimeoutException, không phải là một FaultException. –

0

Thật không may, hoạt động OneWay là điều kiện tiên quyết cho các kênh song công.

+0

Không, điều đó không đúng. Tôi sắp cập nhật câu trả lời. –