简体   繁体   English

如何在没有崩溃应用程序的情况下通过WCF双工服务发送消息?

[英]How to send message through WCF duplex service without crash application?

I have a WCF service for communicating between an Admin PC and multiple Client PCs . 我有一个WCF服务,用于在管理PC多台 客户端PC之间进行通信。

Here the WCF service will be hosted on live while Admin PC and Client PC using application which is build in WPF. 在这里,WCF服务将实时托管,而Admin PC和Client PC将使用WPF中内置的应用程序。 WCF is working as duplex service for handle events and broadcast events to other users. WCF充当双工服务,用于处理事件和向其他用户广播事件。

Like if admin will send message to service then it will be broadcast to all clients while when client send message to service then it will also broadcast to all other users. 就像管理员将消息发送到服务一样,它将被广播到所有客户端,而当客户端将消息发送到服务时,它将也会广播到所有其他用户。

when multiple messages are sending to service then it's crashing the application . 当有多个 消息 发送服务时,将使应用程序崩溃 Here I have put my code below so please check it and suggest me for solve this issue. 在这里,我将代码放在下面,因此请检查并建议我解决此问题。

WCF Service Code WCF服务代码

IBroadcastorService1 Interface: 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 contains below code : 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);
                }
            }
        }
    }
}

} }

And web.config file is like : 和web.config文件就像:

<services>
  <service behaviorConfiguration="Service" name="WcfMultipleCallBacks.BroadcastorService">
    <endpoint address="" binding="wsDualHttpBinding" contract="WcfMultipleCallBacks.IBroadcastorService1" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>

WPF Admin application code. WPF Admin应用程序代码。 I have create a class for handle events relate to WCF service reference : BroadcastorCallback.cs class : 我为与WCF服务参考有关的句柄事件创建了一个类:BroadcastorCallback.cs类:

public class BroadcastorCallback : IBroadcastorService1Callback
{
        private System.Threading.SynchronizationContext synContext = AsyncOperationManager.SynchronizationContext;
        private EventHandler _broadcastorCallBackHandler;


        //SetHandler, is used to set the callback handler for the client.
        public void SetHandler(EventHandler handler)
        {
            this._broadcastorCallBackHandler = handler;
        }

//BroadcastToClient, is used to allow the service to call the client. 
        //When other clients send an event notification to the service, the service will connect to this client 
        //through the callback channel, then call this method to notify this client the event.
        public void BroadcastToClient(EventDataType eventData)
        {
            synContext.Post(new System.Threading.SendOrPostCallback(OnBroadcast), eventData);
        }

        //OnBroadcast, is the connection between the client callback handler, which is set in the first method, 
        //and the actual service call, which will be invoked by the service through the second method. 
        //When the service calls the second method, BroadcastToClient, to notify a event, the call will be delegated to 
        //this method, OnBroadcast, and then the same call will be delegated to the client callback handler.
        public void OnBroadcast(object eventData)
        {
            this._broadcastorCallBackHandler.Invoke(eventData, null);
        }

   }
}

While MainWindow.cs contain code like : 虽然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()
                       });
          }
        }

This code is working perfect when there is message sending is not too much speedy. 当消息发送速度不太快时,此代码可以完美地工作。 But when message communication is too much speedy then it's crashing wpf application. 但是,当消息通信速度太快时,它会使wpf应用程序崩溃。

For testing purpose when I am applying While loop on SendEvent then it's crashing WPF application like. 为了测试目的,当我在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()
                       });
}
          }
        }

I check with setting debugger but don't know which part is making my application crash and why it's happening in speedy communication. 我检查了调试器的设置,但不知道是哪部分使我的应用程序崩溃以及为什么它在快速通信中发生。


This is a summary of my comments above the question. 这是我对问题的评论的摘要。


I think there are a few problems in your code that arrise when you are calling the service under load. 我认为在您的负载下调用服务时,代码中会出现一些问题。

Your service is marked as a singleton , multithreaded-aware service by this WCF ServiceBehavior declaration. 您的服务标记为这个WCF一个多线程感知服务ServiceBehavior声明。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, // singleton
                 ConcurrencyMode = ConcurrencyMode.Multiple)] // thread-safe(?)
public class BroadcastorService : IBroadcastorService1 { ... }

InstanceContextMode.Single is straight-forward as WCF sets this up for you with nothing that you really need to do however ConcurrencyMode.Multiple is a declaration on your part that you can accept multiple concurrent calls by multiple threads. InstanceContextMode.Single非常简单,因为WCF无需您真正要做的任何事情为您进行设置, 但是 ConcurrencyMode.Multiple是您可以接受多个线程进行的多个并发调用的声明 You declare that you accept all responsibility and you don't hold WCF accountable. 您声明自己承担所有责任,并且不对WCF负责。 WCF is trusting you that you won't shoot yourself in the foot. WCF相信您不会踢脚。

Normally it is better to let WCF determine how and when calls from clients make their way into your service code. 通常,最好让WCF确定来自客户端的呼叫如何以及何时进入您的服务代码。 By default, WCF serialises all calls invoking methods on your service one at a time , making your service thread-safe without developers having to do anything with scary lock() s. 默认情况下,WCF 串行化所有呼叫同时调用你的服务的一个方法,使您的服务线程安全的 ,而无需做任何可怕的开发商lock()秒。 When used with InstanceContextMode it can lead to better scalability of your service host. InstanceContextMode使用时,可以导致服务主机更好的可伸缩性

Consider too that the first thing each of your WCF service methods do is to perform a lock() on the whole hard-coded singleton, you are not getting any benefit from ConcurrencyMode.Multiple . 还要考虑每个WCF服务方法要做的第一件事就是对整个硬编码单例执行lock() ,而ConcurrencyMode.Multiple并没有带来任何好处。 You may as well use ConcurrencyMode.Single , remove all of the lock() s and let WCF do all the serialization of method calls for you. 您也可以使用ConcurrencyMode.Single ,删除所有的lock()然后让WCF为您完成方法调用的所有序列化。 Much safer than manually making your service thread-safe. 比手动使服务成为线程安全的安全得多。 Plus if you ever want to remove the singleton nature of your service and use say InstanceContextMode.PerCall or InstanceContextMode.PerSession , it is argbuably a one-line change. 另外,如果您想删除服务的单例性质并使用InstanceContextMode.PerCallInstanceContextMode.PerSession ,这很可能是单行更改。

When your app is under load, the fact that your service is marked as: 当您的应用加载时,您的服务被标记为:

  • "I can handle any number of concurrent thread calls" :P and “我可以处理任意数量的并发线程调用” :P和
  • "Let all threads execute against the same service object" :P “让所有线程针对同一个服务对象执行” :P

...can result in a very risky service. ...可能会导致非常危险的服务。 By making the above suggested changes you effectively throttle the number of concurrent calls from all your clients thus making the service more stable . 通过进行上述建议的更改,您可以有效地限制来自所有客户端的并发呼叫数,从而使服务更加稳定

Crashes, Exception Handling and Debugging Tips 崩溃,异常处理和调试提示

You mentioned that your app is crashing but you didn't say what the error was. 您提到您的应用程序崩溃了,但您没有说明错误是什么。 Looking at your code I can see alot of: 查看您的代码,我可以看到很多:

try
{
    // something here
}
catch (Exception ex)
{
}

Generally you want to avoid doing this sort of thing because you are telling .NET that you want to silently catch all exceptions. 通常,您要避免做这种事情,因为您是在告诉.NET您要静默捕获所有异常。 You as the developer aren't even being informed of possibly nasty bugs in your code. 作为开发人员,您甚至都没有意识到代码中可能存在讨厌的错误。 Catching all exceptions is rather naughty as you really want to just catch the ones you are expecting and perhaps setup a unhandled exception handler for everything else elsewhere in your code that simply displays a fatal message to the user before shutting down the app somewhat gracefully . 捕获所有异常是相当调皮 ,你真的想正好赶上您所期待的那些,或许设置了一切别的地方在你的代码中未处理的异常处理程序 ,只显示一个致命的消息给用户关闭应用程序有点优雅前。

To improve your debugging, make sure you run your app in the Visual Studio debugger. 要改善调试,请确保在Visual Studio调试器中运行应用程序。

From the Debug menu, choose Debug.Windows.Exception Settings . 从“ 调试”菜单中,选择“ Debug.Windows.Exception设置”

In the tool window that appears, tick the box Common Language Runtime Exceptions . 在出现的工具窗口中,选中“ 公共语言运行时例外 ”框。 This tells VS that you want to be informed of all CLR exceptions. 这告诉VS,您想被告知所有 CLR异常。 (later you can go in and just pick the ones you want) (稍后您可以进入,然后选择您想要的人)

Now whenever an exception is thrown, the debugger will stop and place the cursor on the offending line. 现在,每当引发异常时,调试器就会停止并将光标放在有问题的行上。 Note: at this point this is what is known as a first-chance exception as the debugger stops immediately on the line. 注意:这是所谓的“ 第一次机会异常”,因为调试器立即停止在线。 Let's imagine a TimeoutException was thrown. 让我们想象一下抛出了TimeoutException It's not necessarily an error as you might say have a catch (TimeoutException) somewhere. 这不一定是错误,因为您可能会说某个地方有一个catch (TimeoutException) It has not yet progressed to the first catch() (if any) block so don't be alarmed. 它尚未进行到第一个catch() (如果有的话)块,因此请勿惊慌。 If you press F5 (or Debug.Continue menu), then the debugger will resume the app stopping at your catch(TimeoutException) . 如果按F5 (或Debug.Continue菜单),则调试器将继续在catch(TimeoutException)处停止应用程序。 Now had you not ticked the box in Debug Settings, the debugger would have just gone straight to your catch(TimeoutException) without issuing a first-chance notification. 现在,如果您没有在“调试设置”中打勾,则调试器将直接进入您的catch(TimeoutException)而不会发出优先机会通知。 The problem is now you don't know where the error occurred without looking at the call stack in the Exception object. 现在的问题是,如果不查看Exception对象中的调用堆栈,就不知道错误发生在哪里。

Proxy clients 代理客户

Though probably not an immediate concern, I notice too that your clients are creating a WCF proxy and storing it in a field of your app's MainWindow class. 尽管可能不是紧迫的问题,但我也注意到您的客户端正在创建WCF代理并将其存储在应用程序的MainWindow类的字段中。 The thing about proxies is that they are subject to breaking after a period of time and WCF is no exception. 关于代理的事情是,它们在一段时间后会破裂,WCF也不例外。 Generally they represent a network connections. 通常,它们代表网络连接。 Networks come and go. 网络来来往往。 Connections can simply timeout if idle and be closed by the server. 如果空闲 ,连接可以简单地超时并被服务器关闭 The client won't know about it until it goes to invoke it by which time it will be too late. 直到它去调用它之前,客户端都不会知道它。 You'll get an xxxException and the proxy will be marked as faulted meaning that it can't be used again . 您将得到一个xxxException并且该代理将被标记为错误 ,这意味着它不能再次使用 You'll need to make another one. 您需要再做一个。

For this reason, it's generally better to create a proxy at the point prior to making your first call, then getting rid of it (you should Dispose() it) once the current batch of calls are complete. 因此,通常最好在进行首次调用之前在该位置创建一个代理,然后在完成当前一批调用后将其删除(您应该使用Dispose() )。 That, or build into your application that it can handle WCF faults and re-create the proxy when it needs to. 这样,或者在您的应用程序中内置它可以处理WCF错误并在需要时重新创建代理。

Now, depending on what WCF binding you are using, timeouts vary, it may be 1, 5 or 10 mins. 现在,根据您使用的WCF绑定,超时可能有所不同,可能是1分钟,5分钟或10分钟。

Again this is just a FYI, I don't think it is happending here but you never know. 同样,这只是一个仅供参考,我不认为这是在这里发生,但您永远都不会知道。

Don't Let the UI Thread Call the WCF Service 不要让UI线程调用WCF服务

OP: OP:

When signal sending is start from Admin side to service then I am not able to do any thing on admin screen. 从管理员端开始向服务发送信号时,我无法在管理员屏幕上执行任何操作。 Even I can not minimize, maximize or close that screen 甚至我也无法最小化,最大化或关闭该屏幕

Your Admin client is freezing because you are calling the WCF service from your btnSendEvent_Click handler. 您的Admin客户端处于冻结状态,因为您正在从btnSendEvent_Click处理程序调用WCF服务。 The UI won't do anything until that method returns. 在该方法返回之前,UI不会执行任何操作。 This is the nature of all UI. 这是所有UI的本质。 No UI is multithreaded. 没有 UI是多线程的。 The fact that your click handler is performing an expensive and timely network call is just going to make your UI more notiecably unresponsive. 您的点击处理程序正在执行昂贵且及时的网络呼叫这一事实只会使您的UI明显变得无响应。 Perhaps you need to call it inside a worker thread that the BackgroundWorker component provides (novice easier) or asynchronously via async/await (better). 也许您需要在BackgroundWorker组件提供的工作线程(更容易完成更改)中调用它,或者通过async/await (更好)进行async/await调用。

OP: OP:

Thank you very much for your support. 非常感谢您的支持。 Right now I have use BackgroundWorker in Admin side application and apply changes on WCF service as per your instruction. 现在,我已在管理端应用程序中使用BackgroundWorker ,并按照您的指示将更改应用于WCF服务。 Now it's sending signal smoothly . 现在它正在 平稳地 发送信号。 without crash and freezing of Admin Side application. 不会崩溃和冻结管理端应用程序。

I am glad to hear that my suggestion helped solved the issue. 我很高兴听到我的建议帮助解决了这个问题。

Tell me more 告诉我更多

I highly recommend this excellent WCF bible in either book/Kindle form: 我强烈建议以书籍/ Kindle形式使用这本出色的WCF圣经:

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM