wcfpipeduplex

Detect disconnect in WCF


We're building a WCF server (.NET 4.5). It will only use net.pipe transport.

When a client closes the PIPE connection, the server gets unhandled CommunicationException, and terminates.

Q1. How do I handle the CommunicationException so the server does not terminate and continues serving other clients?

Q2. In the handler, how do I get SessionId of the session that was aborted? I need this to do clean up some session-specific data.

Thanks in advance!

contract

 [ServiceContract(CallbackContract = typeof(IContractCallback))]
 public interface IContractServer
{
[OperationContract(IsOneWay = true)]
void Connect(bool status);

[OperationContract(IsOneWay = false)]
void Disconnect(IContractServer _channelCallback);

[OperationContract(IsOneWay = true)]
void Play(bool status);
}

service

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IContractServer
{
public List<IContractCallback> _channeList = new List<IContractCallback>();

public void Connect(bool status)
{
    IContractCallback a = OperationContext.Current.GetCallbackChannel<IContractCallback>();
    int call = 0;
    foreach (var callBack in _channeList)
    {
        if (callBack == a)
        {
            call++;
        }
    }

    if (call == 0)
    {
        _channeList.Add(a);
        a.ConnectCallback(true);
    }
    else
    {
        a.ConnectCallback(false);
    }
}

public void Disconnect(IContractServer _channelCallback)
{
    foreach (var contractCallback in _channeList)
    {
        if (contractCallback == _channelCallback)
        {
            _channeList.Remove(contractCallback);
        }
    }
}

public void Play(bool status)
{
    foreach (var contractCallback in _channeList)
    {
        contractCallback.PlayCallback(status);
    }
}
}

client

using System.ComponentModel;
using System.ServiceModel;
using System.Windows;
using Host;

namespace VideoPlayer
{
 public partial class MainWindow : Window, IContractCallback
{
private IContractServer Proxy = null;
public MainWindow()
{
    InitializeComponent();
    InstanceContext context = new InstanceContext(this);
    DuplexChannelFactory<IContractServer> factory = new DuplexChannelFactory<IContractServer>(context, new NetNamedPipeBinding(), "net.pipe://localhost");
    Proxy = factory.CreateChannel();
    Proxy.Connect(true);
}

public void ConnectCallback(bool status)
{
    MessageBox.Show(status ? "connected" : "no connected");
}

public void PlayCallback(bool status)
{
    if (status)
    {
        MessageBox.Show("status true");
    }
    else
    {
        MessageBox.Show("status false");
    }
}


private void ButtonPlay(object sender, RoutedEventArgs e)
{
    Proxy.Play(true);
}


private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
    //хочу отправить сообщение о закрытии
    Proxy.Disconnect(Proxy);
}

Solution

  • I faced with this problem before in my duplex services when an event raised from server side the exception occurred if there was no alive channel between client and server so server dropped to Fault state and all next requests won't be responded,

    For this reason I came to this conclusion to check the channel and if it was alive then let call back methods to be raised.

    In service side the trick would be like ↓

    bool IsChannelAlive()
    {
        Logging logging = new Logging(LogFile);
        try
        {
            if (((ICommunicationObject)_callbackChannel).State == CommunicationState.Opened)
            {
                logging.Log(LoggingMode.Prompt, "Channeld is still alive, can raise events...");
                return true;
            }
        }
        catch (Exception exp)
        {
            logging.Log(LoggingMode.Error, "IsChannelAlive()=> failed, EXP: {0}", exp);
        }
        logging.Log(LoggingMode.Warning, "Channeld is not alive so events won't raised...");
        return false;
    }
    

    and in one of my events I use it like :

    void stran_OperationTimedOut(object sender, EventArgs e)
    { 
        if (IsChannelAlive())
            _callbackChannel.OnOperationTimedOut(); 
    }
    

    But for a while I use this trick to know closed channel to do something:

    public ImportService()
    {
        //Handle ContextClose to Audit all actions made in session
        OperationContext.Current.InstanceContext.Closed += delegate
        {
            //Here         
        };  
    }
    

    which is not reliable.

    I am still using that IsAliveChannel() in my services.

    Hope this answer resolve your problem or give you the clue.