我对WCF很陌生,并且正在尝试启动并运行业务需求。为了给你一些背景知识,我需要一个可以通过.NET产品与其他进程(进程间通信)进行通信的Windows服务。我们的想法是,在我们所有的产品中,任何时候只能运行一个计时器,中央代理将根据用户想要运行的程序告诉每个程序启动/停止它。
目前我只想让同一个客户端项目运行两次,以便在将其扩展到数百个其他项目之前启动/停止彼此的计时器。
我得到的错误低于第二个客户端连接并调用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)
接口
回调接口:
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
答案 0 :(得分:0)
我已经对我的问题进行了相当多的调查,并发现这是由于我循环遍历所有打开的连接以告诉他们停止计时器,包含连接当前正在调用我的事件,因此尝试访问回调通道会导致阻塞。
您无法在处理请求期间访问回调频道
我实施了以下代码更改来解决问题:
之前:
try
{
con.StopTimer(kvp.Key);
}
catch { }
后:
try
{
if (OperationContext.Current.GetCallbackChannel<ITimeRecordingClient>() != con)
{
con.StopTimer(kvp.Key);
}
}
catch { }