本文介绍了WCF双工TCP通信错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个示例服务来测试WCF net.tcp通信.这是一项非常简单的服务,它所做的就是向客户端订阅该服务,然后调用callbackchannel来通知所有已连接的客户端有关广播消息的信息.该服务托管在IIS 7.5中.

I have a sample service to test WCF net.tcp communication. It is very simple service and all it does is subscribing a client to the service and then calls callbackchannel to notify all connected clients about broadcasted message. The service is hosted inside IIS 7.5.

这里是服务代码,请测试客户端进行测试.

Here is service code and test client to test it.

[ServiceContract(CallbackContract = typeof(ISampleServiceCallBack), SessionMode = SessionMode.Required)]
public interface ISampleCuratioService
{
    [OperationContract(IsOneWay = true)]
    void SubcribeToService(string sub);

    [OperationContract]
    string GetData(int value);

    [OperationContract(IsOneWay = true)]
    void Broadcast(string message);
}

public interface ISampleServiceCallBack
{
    [OperationContract(IsOneWay = true)]
    void NotifyClient(string message);
}

这是服务实现:

[ServiceBehavior(Name = "CuratioCSMService", InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : ISampleCuratioService
{
    private static List<ISampleServiceCallBack> JoinedClien = new List<ISampleServiceCallBack>();

    public void SubcribeToService(string sub)
    {
        var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>();
        if (!JoinedClien.Contains(subscriber))
        {
            JoinedClien.Add(subscriber);
        }
    }

    public string GetData(int value)
    {
        return string.Format("You entered: {0}", value);
    }

    public void Broadcast(string message)
    {
        JoinedClien.ForEach(c => c.NotifyClient("message was received " + message));
    }
}

我无法理解运行它时的行为.在第一个客户端运行之后,一切正常,但是当我关闭并打开测试客户端应用程序时,它引发异常,通知该通道由于处于故障状态而无法用于通信.

I can not understand the behavior I get when running it. After the first client runs everything works fine but as I close and open test client app, it throws exception notifying that channel can not be used for communication as it is in fault state.

这是示例测试客户端:

    static void Main(string[] args)
    {
        var callneckclient = new ServiceClientProxy();
        var client = new SampleCuratioServiceClient(new InstanceContext(callneckclient));
        client.SubcribeToService("me");
        Console.ReadLine();

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(client.GetData(5));
            client.Broadcast("this is from client me");
        }
        client.Close();
        Console.Read();
    }

    public class ServiceClientProxy : ISampleCuratioServiceCallback, IDisposable
    {
        public void NotifyClient(string message)
        {
            Console.WriteLine(message);
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
    }

当我运行5个客户端时,情况甚至出现了问题.那些都不发送或接收消息.

The situation gets even buggy when I run 5 clients. Non of those send or receive messages.

推荐答案

客户端调用SubcribeToService时,会将其操作上下文添加到名为JoinedClien的列表中.

When a client calls SubcribeToService you add its operation context to a List called JoinedClien.

当您在服务器中调用Broadcast时,您将在所有已收集的操作上下文中为曾经连接过的每个客户端调用方法NotifyClient.

When you call Broadcast in your server, you call the method NotifyClient on all collected operation contexts for every client that has ever connected.

问题是,断开连接的客户端不会从您的JoinedClien列表中删除.当您尝试在断开连接的操作上下文中调用操作方法时,会出现通道处于故障状态错误.

The problem is, that a disconnected client won't get removed from your JoinedClien list.When you try to call an operation method on a disconnected operation context, you get the channel is in faulted state error.

要变通,应订阅Channel_ClosedChannel_Faulted事件,并在回叫客户端并删除有故障客户端的操作上下文时捕获CommunicationException:

To work around, you should subscribe to the Channel_Closed and Channel_Faulted events and also catch the CommunicationException when calling back into your clients and remove the operation context of the faulted clients:

public void Broadcast(string message)
{
    // copy list of clients
    List<OperationContext> clientsCopy = new List<OperationContext>();
    lock(JoinedClien) {
        clientsCopy.AddRange(JoinedClien);
    }

    // send message and collect faulted clients in separate list
    List<OperationContext> clientsToRemove = new List<OperationContext>();
    foreach (var c in JoinedClien)
    {
        try {
            c.NotifyClient("message was received " + message));
        }
        catch (CommunicationException ex) {
            clientsToRemove.Add(c);
        }
    }

    foreach (var c in clientsToRemove)
    {
        lock(JoinedClien) {
            if(JoinedClien.Contains(c))
                JoinedClien.Remove(c);
        }
    }
}

添加新客户端时,您也必须锁定该操作:

When adding new clients you have to lock that operation, too:

var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>();
lock(JoinedClien)
{
    if (!JoinedClien.Contains(subscriber))
    {
        JoinedClien.Add(subscriber);
    }
}

这篇关于WCF双工TCP通信错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 12:36
查看更多