Click here to Skip to main content
12,627,316 members (36,576 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

4.3K views
16 bookmarked
Posted

Implement A Duplex Service Using WCF --- Beginner's Tutorial For WCF

, 3 Nov 2015 CPOL
Rate this:
Please Sign up or sign in to vote.
The blog documented problems encountered in the process of learning WCF and Computer Network.

Introduction

At the beginning of this article, let's talk about the messaging transfer mode in WCF.

Messaging TransferMode

  •  Request-Reply​ - By default, all WCF will operated in Request-Reply mode. It means that, when client make a request to the WCF service and client will wait to get response from service (till receive Timeout, defaultl value is 1 minute, you can change it By ReceicedTimeOut object). If service doesn't responed to the service within received timeout, Client will receive TimeOutException.

          Request-Reply

 

 

 

  • ​One-Way - In One-Way transfer mode, client will send a request to the server and does not care whether it is success or failure of service execution. These is no return from the server side, it is one-way commucation. Client will be blocked only for a moment till it dispatches its call to service. If any exception thrown by service will not reach the client.

     ​     Client will continue to execute its statement, after making one-way call to server. There is no need to wait, till server execute. But sometime when one-way calls reach the service, they may not be dispatched one at a time, according to the service's configured concurrency mode behavior. If the number of queued messages has excessed the queue's capacity, the client will be blocked even if it's issued a one-way call.

 

 One-Way operation can be enabled by setting IsOneWay property to true in Operation cantract attribute.
​[ServiceContract]
public interface IMyService
{
    [OperationContract(IsOneWay=true)]
    void MyMethod(string parameter);
}

You also can use the One-Way communication with Sessionful service.     

[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
    [OperationContract(IsOneWay = true)]
    void MyMethod();
}
  • Callback Service - Till now, we have seen that all cilents  will call the service to get the things done. But WCF also provides the service to call the client. So​ we call it duplex transfer mode.

Callback Service​

​       Note
  • HTTP protocols are connectionless nature, so it is not supported for callback operation. So BasicHttpBinding and WSHttpBinding cannot be used for callback service.
  • WSDualHttpBinding is made for call back operation.
  • All this bindings include TCP and IPC protocols support Duplex communication.

Step 1 : Configuring a callback contract

Indeed it define what operations the client should have.

//IPushCallback.cs
​namespace WCFTraining.DuplexStreamingService
{
    public interface IPushCallback
    {
        [OperationContract(IsOneWay = true)]
        void ReceiveData(string data);

        [OperationContract(IsOneWay = true)]
        void ReceiveStream(Stream stream);

        [OperationContract(IsOneWay = true)]
        void ReceiveLog(List<string> log);
    }
}

Step 2 : Define a service and it's implement

Callback service can be enabled by using CallbackContract property in the ServiceContract attribute. Let's implement a difficult demo.
//IDuplexService.cs
namespace IISHost.DuplexStreamingService
{
    [ServiceContract(CallbackContract = typeof(IPushCallback))]
    public interface IDuplexService
    {
        //Duplex operation
        [OperationContract(IsOneWay = true)]
        void StartPushingData();

        [OperationContract(IsOneWay = true)]
        void StopPushingData();

        [OperationContract(IsOneWay = true)]
        void StartPushingStream();

        [OperationContract(IsOneWay = true)]
        void StopPushingStream();

        //Logging
        [OperationContract(IsOneWay = true)]
        void GetLog();
    }
}
Implement  the operation in the service.
//DuplexService.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Threading;

namespace WCFTrrainning.DuplexStreamingService
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
    public class DuplexService : IDuplexService
    {
        List<string> log = new List<string>();
        static bool continuePushingData;

        #region Duplex operation
        public void StartPushingData()
        {
            log.Add("StartPushingData");
            continuePushingData = true;
            IPushCallback pushCallPackChannel = OperationContext.Current.GetCallbackChannel<IPushCallback>();
            ThreadPool.QueueUserWorkItem(new WaitCallback(PushData), pushCallPackChannel);
        }

        public void StopPushingData()
        {
            log.Add("StopPushingData");
            continuePushingData = false;
        }

        public void StartPushingStream()
        {
            log.Add("StartPushingStream");
            IPushCallback pushCallbackChannel = OperationContext.Current.GetCallbackChannel<IPushCallback>();
            ThreadPool.QueueUserWorkItem(new WaitCallback(PushStream), pushCallbackChannel);
        }

        public void StopPushingStream()
        {
            log.Add("StopPushingStream");
            localStream.StopStreaming = true;
        }

        public void GetLog()
        {
            IPushCallback pushCallbackChannel = OperationContext.Current.GetCallbackChannel<IPushCallback>();
            pushCallbackChannel.ReceiveLog(log);
        }

        private static void PushData(object state)
        {
            IPushCallback pushCallPackChannel = state as IPushCallback;

            do
            {
                pushCallPackChannel.ReceiveData(CreateInterestingString(rand.Next(4, 256)));
            } while (continuePushingData);

            pushCallPackChannel.ReceiveData("Last Message");
        }

        void PushStream(object state)
        {
            IPushCallback pushCallbackChannel = state as IPushCallback;
            localStream = new FlowControlledStream();
            localStream.ReadThrottle = TimeSpan.FromMilliseconds(800);

            pushCallbackChannel.ReceiveStream(localStream);
        }

        #endregion
    }
}

the service will host with IIS. So we need to edit our web.config

<version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true">
    </compilation>
  </system.web>
    <system.serviceModel>
      <services>
        <service name="IISHost.DuplexStreamingService.DuplexService">
          <endpoint address="" binding="netTcpBinding" contract="IISHost.DuplexStreamingService.IDuplexService"></endpoint>
          <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"></endpoint>
        </service>
      </services>
      <behaviors>
        <serviceBehaviors>
          <behavior name="">
            <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults="false"/>
          </behavior>
        </serviceBehaviors>
      </behaviors>
    </system.serviceModel>
    <system.webServer>
        <directoryBrowse enabled="true" />
    </system.webServer>
</configuration>

Client Sides

Step 3 : Implement the Callback contract

//ClientReceiver.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using DuplexStreamingService;

namespace DuplexStreamingClient
{
    public class ClientReceiver : IPushCallback
    {
        public ManualResetEvent LogReceived { get; set; }
        public ManualResetEvent ReceiveDataInvoked { get; set; }
        public ManualResetEvent ReceiveDataCompleted { get; set; }
        public ManualResetEvent ReceiveStreamInvoked { get; set; }
        public ManualResetEvent ReceiveStreamCompleted { get; set; }
        public string Name { get; set; }

        public List<string> ServerLog { get; set; }

        public ClientReceiver()
        {
            LogReceived = new ManualResetEvent(false);
            ReceiveDataInvoked = new ManualResetEvent(false);
            ReceiveDataCompleted = new ManualResetEvent(false);
            ReceiveStreamInvoked = new ManualResetEvent(false);
            ReceiveStreamCompleted = new ManualResetEvent(false);
            Name = "ClientReceiver_" + DateTime.Now;
        }

        public void ReceiveData(string data)
        {
            Console.WriteLine("[{0}] ReceiveData received the following:", Name);
            Console.WriteLine(data);
            ReceiveDataInvoked.Set();
            if (data == "LastMessage")
            {
                ReceiveDataCompleted.Set();
            }
        }

        public void ReceiveStream(Stream stream)
        {
            ReceiveStreamInvoked.Set();

            int readResult;
            byte[] buffer = new byte[1000];
            do
            {
                try
                {
                    readResult = stream.Read(buffer, 0, buffer.Length);
                    Console.WriteLine("[{0}] just read {1} bytes.{2}stream.Position = {3}", this.Name, readResult, Environment.NewLine, stream.Position);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Caught exception when trying to read: {0}", ex);
                    throw;
                }
            }
            while (readResult != 0);

            stream.Close();

            Console.WriteLine("[{0}] ReceiveStream invoked.", Name);
            ReceiveStreamCompleted.Set();
        }

        public void ReceiveLog(List<string> log)
        {
            ServerLog = log;
            LogReceived.Set();
        }
    }
}

step 4 : Generate a client to call the service.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace DuplexStreamingClient
{
    class Program
    {
        static void Main(string[] args)
        {
            RunTests();
        }

        private static void RunTests()
        {
            string address = "net.tcp://localhost/IISHost/DuplexStreamService/DuplexService.svc";
            NetTcpBinding binding = new NetTcpBinding();
            ClientReceiver clientReceiver = new ClientReceiver();

            DuplexChannelFactory<IDuplexService> channelFactory = new DuplexChannelFactory<IDuplexService>(new InstanceContext(clientReceiver), binding, address);
            IDuplexService client = channelFactory.CreateChannel();

            Console.WriteLine(client.DownloadData());

            // Test Duplex Operation
            Console.WriteLine("Invoking StartPushingStream - get the server to push a stream to the client.");
            client.StartPushingStream();

            Console.WriteLine("Waiting on ReceiveStreamInvoked from the ClientReceiver.");
            clientReceiver.ReceiveStreamInvoked.WaitOne();
            clientReceiver.ReceiveStreamInvoked.Reset();

            Console.WriteLine("Invoking StopPushingStream");
            client.StopPushingStream();
            Console.WriteLine("Waiting on ReceiveStreamCompleted from the ClientReceiver.");
            clientReceiver.ReceiveStreamCompleted.WaitOne();
            clientReceiver.ReceiveStreamCompleted.Reset();

            Console.WriteLine("Getting results from server via callback.");
            client.GetLog();
            clientReceiver.LogReceived.WaitOne();

            Console.WriteLine("----Following are the logs from the server:-----");
            foreach (string serverLogItem in clientReceiver.ServerLog)
            {
                Console.WriteLine(serverLogItem);
            }
            Console.WriteLine("---------------- End server log. ---------------");

            ((IChannel)client).Close();

            Console.WriteLine("Test passed.");
        }
    }
}
It's the basic usage for duplex transfer mode in WCF. Farther we will talk about some advanced Scenario for Duplex. Like Build a WebSocket with WCF and Broadcast Service with WCF.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Damon Du
Software Developer (Junior)
China China
Just graduated from collage. Now I am trying to practice my English. Sometimes I cannot present the ideas what in my mind. But I believe it will get better if I insist on write tech article

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun4-Nov-15 1:37
memberHumayun Kabir Mamun4-Nov-15 1:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161205.3 | Last Updated 3 Nov 2015
Article Copyright 2015 by Damon Du
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid