0
votes

I want to create a duplex NetTcp service. I am not sure what is the best approach. I have a client that periodically sends it's status to the server and I have a server that has to send periodically data that is independent of any client request. Because i want avoid two connections and don't know anything about the clients at the server, I have to use the connection that is opened by the client. So like I said the client sends periodically status information. But how do I use the connection channel established from the client independantly for sending data to the client. Also the data send from server to client does need a response.

[ServiceContract(CallbackContract = typeof(IStatusServiceCallBack))]
public interface IStatusService
{
    [OperationContract]
    void SendStatus(int statusCode, string statusMessage);
}

public interface IStatusServiceCallBack
{
    // I know IsOneWay=true cannot work, but how to return value????
    [OperationContract(IsOneWay = true)]
    int SendPayTransaction(PayTransaction payTransaction);
}

public class StatusService : IStatusService
{
    public IStatusServiceCallBack Proxy
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel
            <IStatusServiceCallBack>();
        }
    }

    public void SendNotification(int statusCode, string statusMessage)
    {
        Console.WriteLine($"\nClient status : {(statusCode)} {statusMessage}");
    }
    // Is this possible???
    public int SendPayTransaction(PayTransaction payTransaction){
        return Proxy.SendPayTransaction(payTransaction)
    }

}


class Program
{
    static void Main(string[] args)
    {
        var svcHost = new ServiceHost(typeof(NotificationService));
        svcHost.Open();
        bool closeService = false;
        do{
            string command = Console.ReadLine();
            if(command == "Send"){
               // Is this possible???????
               int result = svcHost.SendPayTransaction(new PaymentTransaction(){Amount = 5.50});
               Console.WriteLine($"Result of payment : {result}");
            } else if (command == "exit"){
               closeService = true;
            }
        }while(!closeService);
    }
}
1

1 Answers

0
votes

You're right that you need to use connection established by client. In order to do this you have to perform several steps:

  1. Make sure you have PerSession (or Singleton) instance context mode for your service. The PerCall one will not allow you to maintain permanent connection between client and server.
  2. Ensure that ReceiveTimeout on your binding is long enough. It should be longer than period between client requests, so that idle connection is not terminated by WCF infrastructure.
  3. In your service class you have to remember callback channel. This has to be done in the method that is called by client. So, the result of OperationContext.Current.GetCallbackChannel<IStatusServiceCallBack>() should be saved into private field of StatusService when you are in SendNotification method. If your service has PerSession instance mode, then each service class will have exactly one client. In case of Singleton service you need a collection of callbacks.
  4. It is OK to have return value for callback method, but you need a callback channel object (saved at step 3) to call it.
  5. If you want to call callback method on all clients, then you should keep all client callbacks in the static field of StatusService and then call method on all of them.
  6. If you want to call callback method only on particular client, then you need a way to distinguish clients (ID, Name, whatever) and keep references to callback channels in a dictionary. In case your service has PerSession instance context mode you cannot access service object directly, so it is still good idea to distinguish clients somehow and access them via static field or something. You can also inject your custom factory to create service objects and then have access to them (assuming that factory also registers these objects somewhere).

Beware that if client disconnects it does not invalidate callback channel immediately. You will find that out only when you call callback method - exception will be thrown back.