2016-07-15 67 views
1

我正在開發使用WPF和WCF的客戶端/服務器應用程序。 服務器應用程序託管一個WCF服務,負責執行客戶端請求並在出現問題時回調它們。在忙於長時間運行的操作時Ping應答WCF服務

服務接口與所有的OneWay操作定義一個雙工回調合同。

(簡體)IService

[ServiceContract(CallbackContract = typeof(ISrvServiceCallback))] 
public interface ISrvService 
{ 
    [OperationContract(IsOneWay = true)] 
    void Ping(); 

    [OperationContract(IsOneWay = true)] 
    void LongRunningOperation(); 
} 

public interface ISrvServiceCallback 
{ 
    [OperationContract(IsOneWay = true)] 
    void PingReply(); 

    [OperationContract(IsOneWay = true)] 
    void LongRunningOperationStatus(string reply); 
} 

的服務需要十個分量某些對象根據客戶端的調用改變狀態。出於這個原因,我決定有一個單身人士的服務。

(簡體)服務

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class SrvService : ISrvService 
{ 
    MyObject statusObject; 

    public void LongRunningOperation() 
    { 
     //calling back the client reporting operation status 
     OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>() 
     .LongRunningOperationStatus("starting long running application"); 

     statusObject.elaborateStatus(); 

     //calling back the client reporting object status 
     OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>() 
     .LongRunningOperationStatus("object status: " + statusObject.ToString()); 
    } 

    public void Ping() 
    { 
     OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>().PingReply(); 
    } 


    public SrvService() 
    { 
     statusObject= ...statusObject init... 
    } 

} 

正如你可以看到我有一個操作由服務暴露了一個客戶端調用(每5秒),以檢查是否在服務器應用程序上可用網絡(每個客戶端有一個服務器連接圖標紅色 =服務器不可用,綠色 =服務器不可用)。

當客戶端請求長時間運行的操作,啓動服務器上運行的工作,不能回覆ping請求(客戶端的服務器連接圖標變爲紅色)。

一旦長時間運行的操作完成時,服務器迴應所有由客戶提出的要求和服務器連接圖標重新變綠)。

我想找到一種方法來開發服務,以便服務器始終迴應ping請求,也是在長時間運行時。

  • 我怎麼能做到這一點考慮,我需要保持 InstanceContextMode.Single到十個分量的 服務對象的狀態?
  • 是否有其他/更好的方法來ping WCF服務 可用性和可視化顯示客戶端上的結果?

回答

1

對於單例服務,您將需要多線程實現您的服務器實例以獲得所需的行爲,至少您需要在單獨的線程上運行LongRunningOperation。如果這個操作本質上不是線程安全的,那麼在實現中需要特別注意鎖定或信號量等多重併發調用。當客戶端調用LongRunningOperation()時,這種方式會在單獨的線程中執行,並可以自由地響應ping請求。

有很多方法可以實現這一點。順便提一句,你的問題似乎是客戶端正在進行異步調用(因爲它似乎在等待LongRunningOperation返回時發出ping請求) - 所以我也假設你有一些異步編程的知識。WCF有一些built in ways of handling concurrency,但大多數文檔不包含單例實例,因此您需要仔細閱讀並專注於特殊情況。

我已經與異步/ AWAIT模式最成功的(見herehere) - 一旦被正確設置我有一個狀態單獨服務長期運行的服務電話一個非常可靠和可預測的模式。另外,就ping而言,你應該指出你只是顯示用戶的連接狀態,但是如果你有計劃使用它來進行控制(在打電話之前檢查服務是否在線) there is a lot of discussions here on why you should avoid it.

編輯:與異步快速示例/等待

[ServiceContract] 
public interface ISrvService() 
{ 
    [OperationContract] 
    bool Ping(); // doesnt need to be async 

    [OperationContract] 
    Task<string> LongRunningOperation(); 
} 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class SrvService : ISrvService 
{ 
    MyObject statusObject; 

    public async Task LongRunningOperation() 
    { 
     // lock/semaphore here if needed 
     await Task.Run(() => statusObject.elaborateStatus()); // you could impliment elaborateStatus() as an async Task method and call it without Task.Run 
     return statusObject.ToString(); 
    } 

    public bool Ping() 
    { 
     return true; 
    } 


    public SrvService() 
    { 
     statusObject= ...statusObject init... 
    } 

} 
public class SrvClient : ClientBase<ISrvService> 
{ 
    public async Task<string> LongRunningOperation() 
    { 
     return await base.Channel.LongRunningOperation(); 
    } 

    public async Task<bool> Ping() 
    { 
     // note that we still call this with an await. In the client we are awaiting the wcf service call 
     // this is independent of any async/await that happens on the server 
     return await Task.Run(() => base.Channel.Ping()); 
    } 
} 

使用客戶端:

public class SomeApplicationClass() 
{ 
    SrvClient Client; 
    DispatcherTimer PingTimer; 

    public SomeClass() 
    { 
     BasicHttpBinding binding = new BasicHttpBinding(); 
     EndpointAddress endpoint = new EndpointAddress(
      "http://...:8000/Service/Address"); 
     OutpostClient = new OutpostRemoteClient(binding, endpoint); 

     // pingTimer setup 

    } 

    // async voids are scary, make sure you handle exceptions inside this function 
    public async void PingTimer_Tick() 
    { 
     try 
     { 
      await Client.Ping(); 
      // ping succeeded, do stuff 
     } 
     catch // specify exceptions here 
     { 
      // ping failed, do stuff 
     } 
    } 

    public async Task DoTheLongRunningOperation() 
    { 
     // set busy variables here etc. 
     string response = await Client.LongRunningOperation(); 
     // handle the response status here 
    } 
} 

而且this answer似乎有關。

+0

謝謝你的回覆@ plast1k!我對異步知識很少,而且我實際上也在探索這個領域。 客戶端使用調用_ThreadPool.QueueUserWorkItem(checkService)_的_DispatcherTimer_來ping服務。 _checkService_調用_serviceClient.Ping()_。 我會看看你發佈的所有鏈接,看看我能如何解決我的問題。 是否有可能有你的異步/等待單例服務實現的一些示例代碼?只是想知道如何從客戶端調用並從服務器回覆。 – aDone

+0

沒問題。我應該能夠稍後再挖掘一些,如果我找到它,我會編輯我的帖子。 – plast1k

+0

@aDone示例添加,對不起,花了這麼久! – plast1k