2017-02-17 2 views
1

Я очень новичок в WCF и сейчас пытаюсь получить бизнес-требования. Чтобы дать вам некоторый фон, мне нужен сервис Windows, который может разговаривать с другим процессом (межпроцессное общение) над продуктами .NET. Идея состоит в том, что только один таймер может работать в любой момент во всех наших продуктах, а центральный брокер будет сообщать каждой программе о запуске/остановке, на основе которой пользователь хочет запустить.WCF TimeoutException с использованием каналов обратного вызова

На данный момент я просто хочу, чтобы один и тот же клиентский проект запускался дважды, чтобы запускать/останавливать таймеры друг друга, прежде чем расширять его до сотен других проектов.

Ошибка я получаю это, ниже которого происходит, когда второй клиент подключается и вызывает StartTimer:

An unhandled exception of type 'System.TimeoutException' occurred in mscorlib.dll 

Additional information: This request operation sent to net.tcp://localhost:9044/TimeRecordingService did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. 

Первоначально я думал, что эта проблема будет связана с одновременных соединений/максимально разрешенных подключений. Однако после установки ниже в моем приложении App.Config я обнаружил, что проблема все еще произошла.

<connectionManagement> 
    <add maxconnection="500" address="*"/> 
</connectionManagement> 
... 
<serviceBehaviors> 
    <behavior name="ThrottlingIssue"> 
    <serviceThrottling maxConcurrentCalls="500" maxConcurrentSessions="500" /> 
    </behavior> 
</serviceBehaviors> 

Надеюсь, приведенный ниже код будет полезен

У меня есть следующая структура проекта:
Интерфейсы - Магазины интерфейсов для службы WCF и клиента обратного вызова. (C#)
Служба - реализует службу WCF, а также содержит код для развертывания в качестве службы Windows (C#)
Клиент - рассматривает службу WCF и обрабатывает обратный вызов для приостановки его таймера при открытии нового экземпляра (VB.NET)

Интерфейсы
Callback Интерфейс:

public interface ITimeRecordingClient 
{ 
    [OperationContract(IsOneWay = true)] 
    void ReceiveMessage(string userName, string message); 

    [OperationContract(IsOneWay = true)] 
    void StopTimer(string ID); 
} 

служба Интерфейс:

[ServiceContract(CallbackContract = typeof(ITimeRecordingClient))] 
public interface ITimeRecordingService 
{ 
[OperationContract] 
void Login(string Username, string Password); 

[OperationContract] 
void Listen(); 

[OperationContract] 
void StopListening(); 

[OperationContract] 
void StartTimer(string ID); 

[OperationContract] 
void AddTimer(string ID); 
} 

Услуги:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single)] 
public class TimeRecordingServiceImpl : ITimeRecordingService 
{ 
    private ApplicationUser _user; 
    private List<ITimeRecordingClient> _lstCons = new List<ITimeRecordingClient>(); 
    private Dictionary<string, bool> _tmrs = new Dictionary<string, bool>(); 
    public TimeRecordingServiceImpl() 
    { 
    System.Net.ServicePointManager.DefaultConnectionLimit = 200; 
    } 
    public void Login(string Username, string Password) 
    { 
    var user = new ApplicationUser { Username = Username, Password = Password }; 
    _user = user; 

    foreach (ITimeRecordingClient con in _lstCons) 
     con.ReceiveMessage(Username, Password); 
    } 

    public void AddTimer(string ID) 
    { 
    _tmrs.Add(ID, false); 
    } 

    public void StartTimer(string ID) 
    { 
    List<string> lstIDs = new List<string>(); 
    foreach (KeyValuePair<string, bool> kvp in _tmrs) 
    { 
     if (kvp.Key != ID) 
     { 
     foreach (ITimeRecordingClient con in _lstCons) 
     { 
      try 
      { 
      con.StopTimer(kvp.Key); 
      } 
      catch { } 

     } 
       lstIDs.Add(kvp.Key); 
     } 
    } 

    _tmrs[ID] = true; 
    foreach (string strID in lstIDs) 
    _tmrs[strID] = false; 
    } 

    public void Listen() 
    { 
    var connection = OperationContext.Current.GetCallbackChannel<ITimeRecordingClient>(); 
    _lstCons.Add(connection); 
    } 

    public void StopListening() 
    { 
    var con = OperationContext.Current.GetCallbackChannel<ITimeRecordingClient>(); 
    _lstCons.Remove(con); 
    } 
} 

Клиент
Основная форма:

Public Class Form1 
    Private _channelFactory As DuplexChannelFactory(Of ITimeRecordingService) 
    Private _server As ITimeRecordingService 
    Private _strID As String 
    Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing 
     _server.StopListening() 
     _channelFactory.Close() 
    End Sub 

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
     Dim contract = ContractDescription.GetContract(GetType(ITimeRecordingService)) 
     Dim Binding = New NetTcpBinding() With {.TransferMode = TransferMode.Buffered} 
     Dim EndpointAddress = New EndpointAddress("net.tcp://localhost:9044/TimeRecordingService") 
     Dim endPoint = New ServiceEndpoint(contract, Binding, EndpointAddress) 
     Dim clientImpl As New TimeRecordingClientImpl() 
     AddHandler clientImpl.MessageReceived, AddressOf ShowAlert 
     AddHandler clientImpl.RequestStopTimer, AddressOf StopTimer 

     System.Net.ServicePointManager.DefaultConnectionLimit = 200 
     _channelFactory = New DuplexChannelFactory(Of ITimeRecordingService)(clientImpl, endPoint) 
     _server = _channelFactory.CreateChannel() 
     _strID = Guid.NewGuid.ToString() 

     _server.Listen() 
     _server.AddTimer(_strID) 
     _server.StartTimer(_strID) 
     SlsTimer1.ResetClock() 
    End Sub 

    Private Sub StopTimer(ByVal ID As String) 
     If _strID = ID Then SlsTimer1.StopClock() 
    End Sub 
    Private Sub ShowAlert(Title As String, Body As String) 
     Dim info As New DevExpress.XtraBars.Alerter.AlertInfo(Title, Body) 
     AlertControl1.Show(Me, info) 
    End Sub 

    Private Sub SimpleButton1_Click(sender As Object, e As EventArgs) Handles SimpleButton1.Click 
     _server.StartTimer(_strID) 
     SlsTimer1.StartClock() 
    End Sub 
End Class 

Реализация Callback:

Public Class TimeRecordingClientImpl 
    Implements ITimeRecordingClient 

    Public Event MessageReceived(Username As String, Password As String) 
    Public Event RequestStopTimer(ID As String) 
    Public Sub ReceiveMessage(Username As String, Password As String) Implements ITimeRecordingClient.ReceiveMessage 
     RaiseEvent MessageReceived(Username, Password) 
    End Sub 

    Public Sub StopTimer(ID As String) Implements ITimeRecordingClient.StopTimer 
     RaiseEvent RequestStopTimer(ID) 
    End Sub 
End Class 

ответ

0

Я сделал совсем немного расследования в мой проблемы и что из-за меня я зациклился на всех открытых подключениях, чтобы сообщить им, чтобы остановить таймер, что включил соединение, которое в настоящее время вызывает мое событие, и поэтому попытка доступа к каналу обратного вызова вызвала блок.

Вы не можете получить доступ к каналу обратного вызова во время обработки запроса

я реализовал ниже изменения кода, чтобы решить эту проблему:

Перед:

try 
{ 
    con.StopTimer(kvp.Key); 
} 
catch { } 

После:

try 
{ 
    if (OperationContext.Current.GetCallbackChannel<ITimeRecordingClient>() != con) 
    { 
    con.StopTimer(kvp.Key); 
    } 
} 
catch { }