我对WCF非常陌生,现在正试图让业务需求正常运行。为了给你一些背景,我需要一个windows服务,它可以通过.NET产品与其他进程(进程间通信)进行通信。我们的想法是,在我们所有的产品中,只有一个定时器可以在任何时候运行,并且中央代理会根据用户想要运行哪一个来告诉每个程序启动/停止它。使用回调通道的WCF TimeoutException
目前我只想让相同的客户端项目运行两次,以便在将其扩展到数百个其他项目之前启动/停止对方的计时器。
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.
我最初以为这个问题会涉及到并发连接数/最大允许连接数:
我得到的误差低于当第二客户端连接起来,并调用startTimer所发生。然而,在我的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 )
接口
回调接口:
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
回调实现:
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