C# 如何通过WCF双工服务发送消息而不使应用程序崩溃?
我有一个WCF服务,用于在一台管理PC和多台客户端PC之间进行通信 在这里,WCF服务将在使用WPF中内置的应用程序的管理PC和客户端PC上实时托管。WCF作为双工服务处理事件并向其他用户广播事件 就像若管理员将消息发送到服务,那个么它将广播到所有客户端,而当客户端将消息发送到服务时,它也将广播到所有其他用户 当多条消息正在向服务发送时,应用程序将崩溃。在这里,我把我的代码放在下面,所以请检查它,并建议我解决这个问题 WCF服务代码C# 如何通过WCF双工服务发送消息而不使应用程序崩溃?,c#,wpf,wcf,C#,Wpf,Wcf,我有一个WCF服务,用于在一台管理PC和多台客户端PC之间进行通信 在这里,WCF服务将在使用WPF中内置的应用程序的管理PC和客户端PC上实时托管。WCF作为双工服务处理事件并向其他用户广播事件 就像若管理员将消息发送到服务,那个么它将广播到所有客户端,而当客户端将消息发送到服务时,它也将广播到所有其他用户 当多条消息正在向服务发送时,应用程序将崩溃。在这里,我把我的代码放在下面,所以请检查它,并建议我解决这个问题 WCF服务代码 IBroadcastorService1接口: [Servic
IBroadcastorService1
接口:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract =
typeof(IBroadcastorCallBack1))]
public interface IBroadcastorService1
{
[OperationContract(IsOneWay = true)]
void RegisterClient(string clientName);
[OperationContract(IsOneWay = true)]
void NotifyServer(EventDataType eventData);
}
public interface IBroadcastorCallBack1
{
[OperationContract(IsOneWay = true)]
void BroadcastToClient(EventDataType eventData);
}
[DataContract]
public class EventDataType
{
[DataMember]
public string ClientName { get; set; }
[DataMember]
public string EventMessage { get; set; }
}
BroadcastorService.svc.cs包含以下代码:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class BroadcastorService : IBroadcastorService1
{
private static Dictionary<string, IBroadcastorCallBack1> clients = new Dictionary<string, IBroadcastorCallBack1>();
private static object locker = new object();
public void RegisterClient(string clientName)
{
if (clientName != null && clientName != "")
{
try
{
IBroadcastorCallBack1 callback = OperationContext.Current.GetCallbackChannel<IBroadcastorCallBack1>();
lock (locker)
{
//remove the old client
if (clients.Keys.Contains(clientName))
clients.Remove(clientName);
clients.Add(clientName, callback);
}
}
catch (Exception ex)
{
}
}
}
public void NotifyServer(EventDataType eventData)
{
lock (locker)
{
var inactiveClients = new List<string>();
foreach (var client in clients)
{
if (client.Key != eventData.ClientName)
{
try
{
client.Value.BroadcastToClient(eventData);
}
catch (Exception ex)
{
inactiveClients.Add(client.Key);
}
}
}
if (inactiveClients.Count > 0)
{
foreach (var client in inactiveClients)
{
clients.Remove(client);
}
}
}
}
}
而MainWindow.cs包含如下代码:
private ServiceReference1.BroadcastorService1Client _client;
public MainWindow()
{
InitializeComponent();
RegisterClient();
}
private delegate void HandleBroadcastCallback(object sender, EventArgs e);
public void HandleBroadcast(object sender, EventArgs e)
{
try
{
var eventData = (ServiceReference1.EventDataType)sender;
if (this.txtEventMessages.Text != "")
this.txtEventMessages.Text += "\r\n";
this.txtEventMessages.Text += string.Format("{0} (from {1})",
eventData.EventMessage, eventData.ClientName);
}
catch (Exception ex)
{
}
}
private void RegisterClient()
{
if ((this._client != null))
{
this._client.Abort();
this._client = null;
}
BroadcastorCallback cb = new BroadcastorCallback();
cb.SetHandler(this.HandleBroadcast);
System.ServiceModel.InstanceContext context = new System.ServiceModel.InstanceContext(cb);
this._client = new ServiceReference1.BroadcastorService1Client(context);
//this._client.RegisterClient(this.txtClientName.Text);
this._client.RegisterClient("Harry Potter");
}
private void btnSendEvent_Click(object sender, RoutedEventArgs e)
{
this._client.NotifyServer(
new ServiceReference1.EventDataType()
{
ClientName = "Harry Potter",
//EventMessage = this.txtEventMessage.Text
EventMessage = count.ToString()
});
}
}
当消息发送速度不太快时,此代码工作正常。但当消息通信速度过快时,wpf应用程序就会崩溃
出于测试目的,当我在SendEvent上应用While循环时,它会像这样破坏WPF应用程序
private bool isRun = false;
private void btnSendEvent_Click(object sender, RoutedEventArgs e)
{
isRun = true;
while(isRun = true)
{
this._client.NotifyServer(
new ServiceReference1.EventDataType()
{
ClientName = "Harry Potter",
//EventMessage = this.txtEventMessage.Text
EventMessage = count.ToString()
});
}
}
}
我使用设置调试器进行检查,但不知道是哪一部分导致我的应用程序崩溃,以及为什么会在快速通信中发生这种情况。这是我对上述问题评论的总结
我认为当您在负载下调用服务时,代码中会出现一些问题 此WCF
ServiceBehavior
声明将您的服务标记为单实例、多线程感知服务
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, // singleton
ConcurrencyMode = ConcurrencyMode.Multiple)] // thread-safe(?)
public class BroadcastorService : IBroadcastorService1 { ... }
InstanceContextMode.Single
是直接的,因为WCF为您设置了它,而您实际上不需要做任何事情但是ConcurrencyMode.Multiple
是您可以接受多个线程的多个并发调用的声明。您声明您接受所有责任,并且不追究WCF的责任。WCF相信你不会射中自己的脚
通常,最好让WCF确定来自客户端的调用如何以及何时进入您的服务代码。默认情况下,WCF将调用服务上方法的所有调用序列化,一次调用一个方法,使您的服务线程安全,而开发人员无需对锁()执行任何操作。当与InstanceContextMode
一起使用时,它可以提高服务主机的可伸缩性
还要考虑到您的每个WCF服务方法所做的第一件事是对整个硬编码单例执行lock()
,您不会从ConcurrencyMode.Multiple
中获得任何好处。您还可以使用ConcurrencyMode.Single
,删除所有lock()
s,并让WCF为您完成所有方法调用的序列化。比手动使服务线程安全要安全得多。另外,如果您想要删除服务的单例性质,并使用InstanceContextMode.PerCall或InstanceContextMode.PerSession,这无疑是一个单行更改
当您的应用程序处于加载状态时,您的服务被标记为:
- “我可以处理任意数量的并发线程调用”:P和
- “让所有线程对同一服务对象执行”:P
try
{
// something here
}
catch (Exception ex)
{
}
通常,您希望避免这样做,因为您告诉.NET您希望以静默方式捕获所有异常。作为开发人员,您甚至没有被告知代码中可能存在的严重错误。捕获所有异常是相当顽皮的,因为您确实希望捕获您期望的异常,并且可能会为代码中其他地方的所有异常设置一个未处理的异常处理程序,该处理程序在关闭应用程序之前会向用户显示一条致命消息
要改进调试,请确保在Visual Studio调试器中运行应用程序
从调试菜单中,选择调试.Windows.Exception设置
在出现的工具窗口中,勾选框公共语言运行时异常。这会告诉VS您希望获得有关所有CLR异常的通知。(稍后你可以进去挑选你想要的)
现在,每当抛出异常时,调试器都会停止并将光标放在有问题的行上注意:在这一点上,这就是所谓的第一次机会异常,因为调试器会立即停止在线。让我们想象一个TimeoutException
被抛出。它不一定是一个错误,正如您可能会说的那样,在某个地方有一个catch(TimeoutException)
。它还没有进入第一个catch()
(如果有)块,所以不要惊慌。如果您按F5(或Debug.Continue菜单),则调试器将恢复应用程序,并在您的catch(TimeoutException)
处停止。现在,如果您没有勾选调试设置中的复选框,调试器将直接转到您的捕获(TimeoutException)
,而不会发出第一次机会通知。现在的问题是,如果不查看Exception
对象中的调用堆栈,您就不知道错误发生在哪里
代理客户端
虽然专业
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, // singleton
ConcurrencyMode = ConcurrencyMode.Multiple)] // thread-safe(?)
public class BroadcastorService : IBroadcastorService1 { ... }
try
{
// something here
}
catch (Exception ex)
{
}