none
How to write a console mode client for duplex binding? RRS feed

  • Question

  • I want to implement a binding with callback (maybe netTcpBindingdumplex or wshttpDual) and I see WCF GUI examples that are always running so the callback can execute.

    However, I want to write a console mode client called logger for netTcpBinding or httpDual that simply writes the single string argument to the console whenever there is a callback.

    (1) What options do I have to keep the console mode program running? I could call sleep but for how long? Infinity? I could go into an infinite loop but that would consume a lot of CPU. I could wait for the user to hit return (with Console.ReadLine) but I think that would block my attempts to execute Console.WriteLine.

    (2) I assume that if I am sleeping, the generated client code will execute the callback on a different thread? Is this true? If not, what function do I call to wait for a callback?

    (3) If the console mode program is aborted, perhaps with a taskkill or a control-c, do I need to shut down the WCF connection (channel?) How do I do detect a taskkill or control-c and shutdown the WCF connection gracefully?

    (4) How do I choose between netTcp and httpDual? Would both work fine?

    Thanks,

    Siegfried


    siegfried heintze


    • Edited by siegfried_ Friday, August 31, 2018 4:14 AM clarify
    Friday, August 31, 2018 3:55 AM

Answers

  • Hi Siegfried_,

    For the first two questions, I am not quite clear about your codes. Could you share WCF GUI example with us so that I could understand what you exactly express completely.

    In my opinion, server-side send message to client-side by callback interface in duplex-mode.

    Console.readline() will not block the message from server.

    Here is a demo how does the duplex-mode work.

    Server-side.

       class Program

        {

            static void Main(string[] args)

            {

                using (ServiceHost sh=new ServiceHost(typeof(MyService)))

                {

                    sh.Open();

                    Console.WriteLine("Service is ready");

                    Console.ReadKey();

                    sh.Close();

                }

            }

        }

        [ServiceContract(Namespace ="mydomain",Name = "demo", ConfigurationName = "isv", CallbackContract = typeof(ICallback))]

        public interface IDemo

        {

            [OperationContract(Action = "post_num", IsOneWay = true)]

            void PostNumber(int n);

        }

        [ServiceContract]

        public interface ICallback

        {

            [OperationContract(Action = "report", IsOneWay = true)]

            void Report(double progress);

        }

     

        [ServiceBehavior(ConfigurationName ="sv")]

        public class MyService : IDemo

        {

            public void PostNumber(int n)

            {

                ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();

                for (int i = 0; i <=n; i++)

                {

                    Task.Delay(500).Wait();

                    double p = Convert.ToDouble(i) / Convert.ToDouble(n);

                    callback.Report(p);

                }

            }

    }

    Client-side.

        class Program

        {

            static void Main(string[] args)

            {

                DuplexChannelFactory<IDemo> factory = new DuplexChannelFactory<IDemo>(new CallbackHandler(), "test_ep");

                IDemo channel = factory.CreateChannel();

                Console.WriteLine("Start to Call");

                channel.PostNumber(15);

                Console.WriteLine("Calling is done");

                Console.ReadLine();

            }

        }

        [ServiceContract(Namespace ="mydomain",Name = "demo", ConfigurationName = "isv", CallbackContract = typeof(ICallback))]

        public interface IDemo

        {

            [OperationContract(Action = "post_num",IsOneWay =true)]

            void PostNumber(int n);

        }

       [ServiceContract]

        public interface ICallback

        {

            [OperationContract(Action = "report",IsOneWay =true)]

            void Report(double progress);

        }

        public class CallbackHandler : ICallback

        {

            public void Report(double progress)

            {

                Console.WriteLine("{0:p0}", progress);

            }

    }

    Result.


    Here is an official example.

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-duplex-contract

    Question 3. In order to close the wcf client/server you should use the using statement or try/catch/finally

    Statement to release the managed resource gracefully.

    Question 4. For the intranet application scenario, you should use the nettcpbinding, on the contrary, we use the wshttpdualbinding for the internet application/B2B application scenario.

    Here is official document explain that which binging should be used in the different application scenario.

    https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff650874(v%3dpandp.10)

    Feel free to let me know if you have any questions.

    Best Regards

    Abraham
    • Marked as answer by siegfried_ Tuesday, September 11, 2018 5:32 PM
    Monday, September 3, 2018 1:29 AM
    Moderator

All replies

  • Hi Siegfried_,

    For the first two questions, I am not quite clear about your codes. Could you share WCF GUI example with us so that I could understand what you exactly express completely.

    In my opinion, server-side send message to client-side by callback interface in duplex-mode.

    Console.readline() will not block the message from server.

    Here is a demo how does the duplex-mode work.

    Server-side.

       class Program

        {

            static void Main(string[] args)

            {

                using (ServiceHost sh=new ServiceHost(typeof(MyService)))

                {

                    sh.Open();

                    Console.WriteLine("Service is ready");

                    Console.ReadKey();

                    sh.Close();

                }

            }

        }

        [ServiceContract(Namespace ="mydomain",Name = "demo", ConfigurationName = "isv", CallbackContract = typeof(ICallback))]

        public interface IDemo

        {

            [OperationContract(Action = "post_num", IsOneWay = true)]

            void PostNumber(int n);

        }

        [ServiceContract]

        public interface ICallback

        {

            [OperationContract(Action = "report", IsOneWay = true)]

            void Report(double progress);

        }

     

        [ServiceBehavior(ConfigurationName ="sv")]

        public class MyService : IDemo

        {

            public void PostNumber(int n)

            {

                ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();

                for (int i = 0; i <=n; i++)

                {

                    Task.Delay(500).Wait();

                    double p = Convert.ToDouble(i) / Convert.ToDouble(n);

                    callback.Report(p);

                }

            }

    }

    Client-side.

        class Program

        {

            static void Main(string[] args)

            {

                DuplexChannelFactory<IDemo> factory = new DuplexChannelFactory<IDemo>(new CallbackHandler(), "test_ep");

                IDemo channel = factory.CreateChannel();

                Console.WriteLine("Start to Call");

                channel.PostNumber(15);

                Console.WriteLine("Calling is done");

                Console.ReadLine();

            }

        }

        [ServiceContract(Namespace ="mydomain",Name = "demo", ConfigurationName = "isv", CallbackContract = typeof(ICallback))]

        public interface IDemo

        {

            [OperationContract(Action = "post_num",IsOneWay =true)]

            void PostNumber(int n);

        }

       [ServiceContract]

        public interface ICallback

        {

            [OperationContract(Action = "report",IsOneWay =true)]

            void Report(double progress);

        }

        public class CallbackHandler : ICallback

        {

            public void Report(double progress)

            {

                Console.WriteLine("{0:p0}", progress);

            }

    }

    Result.


    Here is an official example.

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-duplex-contract

    Question 3. In order to close the wcf client/server you should use the using statement or try/catch/finally

    Statement to release the managed resource gracefully.

    Question 4. For the intranet application scenario, you should use the nettcpbinding, on the contrary, we use the wshttpdualbinding for the internet application/B2B application scenario.

    Here is official document explain that which binging should be used in the different application scenario.

    https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff650874(v%3dpandp.10)

    Feel free to let me know if you have any questions.

    Best Regards

    Abraham
    • Marked as answer by siegfried_ Tuesday, September 11, 2018 5:32 PM
    Monday, September 3, 2018 1:29 AM
    Moderator
  • Abraham:

    Ok, questions 1 & 2 above are non-issues since you claim the read prompt does not interfere.

    New questions:

    (1) Why do you (and others) have two copies of the interface and the implementation source code: one in the client and one in the server? should this not be shared? Can it be shared? Perhaps it should be in a common shared project?

    (2) I'm getting a timeout when the callback tries to execute in the server because it cannot find the implementation of the callback in the client even though I have the following code in the client:

    InstanceContext instanceContext = new InstanceContext(new CallbackHandler()); 
    CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext); 

    (3) Did you use visual studio? How did you create this visual studio project? Did you select WCF service or WCF library to create the server?

    (4) Why do you use the factory in the client instead of using the code generated by visual studio? Does the code generated by visual studio not work for callbacks?

    (5) Please explain the purpose of "mydomain". I don't think this is a C# namespace. Is it part of the URI for the server?

    (6) Where is "test_ep" used? "post_num" and "report"? Are these optional names?

    Please post the rest of your code -- especially the App.config file! I like it but I cannot make it work.

    Thanks

    Siegfried


    siegfried heintze


    • Edited by siegfried_ Saturday, September 8, 2018 6:24 PM
    Saturday, September 8, 2018 5:15 AM
  • OK, something was wrong with that my initial attempt and when I started over with a new project and used the DuplexChannelFactory class in the client (why is this necessary? Why does not the code generator call this for me?), it worked.

    In other words, why does not the first declaration of chatClient work? The code generator knows it is duplex!  And the first declaration works if you are not doing duplex/callbacks!

    This is very confusing!

                var ctx = new InstanceContext(new ChatCallback());
                //var chatClient = new ConsoleModeChatClient.ChatClient.ChatSvcClient(ctx);
                var chatClient = new DuplexChannelFactory<ChatClient.IChat>(ctx, "netTcpBinding.ChatSvc").CreateChannel

    Now that GUI program you get when you import the wcf service interface into the client (svcutil.exe?) also gives you errors (red circles with "x" saying it does not support duplex) as soon as you defined the callback class is very confusing. 

    Apparently, you just have to ignore those red circles and everything works fine (so far for my little demo).


    siegfried heintze

    Tuesday, September 11, 2018 5:44 PM
  • Hi Siegfried_,

    Sorry for the late response.

    Both of the two project types are Console application.

    Here is server-side app.config.

      <system.serviceModel>
        <services>
          <service name="sv">
            <endpoint address="http://localhost:3333" binding="wsDualHttpBinding" contract="isv"/>
          </service>
        </services>
      </system.serviceModel>

    Client-side app.config

      <system.serviceModel>
        <client>
          <endpoint name="test_ep" address="http://localhost:3333" binding="wsDualHttpBinding" contract="isv"/>
        </client>
      </system.serviceModel>

    You could use svcutil.exe or add service reference to generate the client proxy class. But we should implement the callback interface in the client-side.

    Here is the official document(how to generate client proxy class in duplex-mode)

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-access-services-with-a-duplex-contract?view=netframework-4.7.2

    beside, you could also use the class library to share the interface between the client-side and server-side.

    Feel free to let me know if you have any questions

    Best Regards

    Abraham
    Wednesday, September 19, 2018 1:50 AM
    Moderator