Saturday, November 24, 2012

Ad hoc Discovery

In this article we will add an ad hoc discovery mechanism to the DiscoveryChat program using System.ServiceModel.Discovery, an implementation of the WS-Discovery protocol.  In order for a service to be discoverable in an ad hoc manner it needs to respond to incoming probe messages. Ad hoc discovery implies that these probe messages come in through a well known port over UDP multicast.
Step 1: Configuring Service Discovery for the DiscoveryChat Application
DiscoveryChat is a chat application that automatically discovers users on the network using ad hoc or managed discovery via a proxy.
Two simple chat windows with no discovery enabled
Two simple chat windows with no discovery enabled
The first thing we need to do is to enable discovery in the simple chat application.

  1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.
    Watch Out
    Visual Studio 2010 must be run in elevated mode. To do this, right-click the Visual Studio 2010 icon and select Run as Administrator.
  2. Open the starting solution Begin.sln located under AdHocDiscovery folder, choosing the language of your preference (C# or VB) which we can download it from
    1. uploaded
    2. EzzFile
  3.  To enable discovery, you need to add a service behavior on the service that will be discoverable. Open the app.config configuration file from the DiscoveryChat project.
  4. Add a new Service Behavior named DiscoveryBehavior inside the element.
    XML
    <behaviors>
      <serviceBehaviors>
        <behavior name="DiscoveryBehavior">
          <serviceDiscovery />
        </behavior>
      </serviceBehaviors>
    </behaviors
  5. Modify the service description by adding the behaviorConfiguration attribute referencing the DiscoveryBehavior just created.
    XML
    <services>
      Microsoft.Samples.Discovery
    .ChatService"
           behaviorConfiguration="DiscoveryBehavior">
    <endpoint
      address=""
      binding="basicHttpBinding"
      contract="ISimpleChatService"/>
  </service>
</services>
  • Next, you need to add a UDP discovery endpoint.  This is where the discovery probe messages will be processed.
       Probe Messages
    A Probe message is a WS-Discovery message used by a client to search for services on the network by service type. For more information about Probe messages, see section 5.2 of the WS-Discovery Specification.
    XML
    <services>
      <service name="Microsoft.Samples.Discovery.ChatService "
               behaviorConfiguration="DiscoveryBehavior">
        <endpoint
          address=""
          binding="basicHttpBinding"
          contract="ISimpleChatService"/>

        <endpoint
          name="udpDiscoveryEpt"
          kind="udpDiscoveryEndpoint"/>

      </service>
    </services>
    Making Services Discoverable
    The combination of the DiscoveryBehavior and UDP endpoint (specified by the attribute kind = “udpDiscoveryEndpoint”) makes your service discoverable.  The discovery behavior will now respond to incoming UDP probes that match the contract type.
  • Second we will add code to asynchronously search for other chat users on the same subnet using service discovery.
    1. First, you will need a reference to the new discovery assembly.  Right-click the DiscoveryChat project and select Add Reference.
      a. 
      Using the .NET tab, add a reference to System.ServiceModel.Discovery.
      System.ServiceModel.Discovery assembly reference
      System.ServiceModel.Discovery assembly reference
      1. Open the SimpleChat.cs (C#) or SimpleChat.vb (Visual Basic) file from the DiscoveryChat project. You can open the code view by selecting the file and pressing F7.
      2. To perform ad hoc discovery you will need to add a member field of type DiscoveryClient. To do that, perform the following steps:
        a. At the top of the class, add the following namespace directive for System.ServiceModel.Discovery
        C#
        using System.ServiceModel.Discovery;
        Visual Basic
        Imports System.ServiceModel.Discovery
        b. 
        Add the following member variable to the SimpleChat class
        C#
        public partial class SimpleChat : Form
        {
            private DiscoveryClient discoveryClient;
        Visual Basic
        Public Partial Class SimpleChat
            Inherits Form

            Private WithEvents discoveryClient As DiscoveryClient
      3. When the DiscoveryClient locates a service, it will provide an EndpointDiscoveryMetadata instance containing metadata about the service.  You will use this metadata to display the list of available chat users in a list box. To do this, add the PopulateUserList method to the SimpleChat class.
        Note: Because the DiscoveryChat application hosts a service, the DiscoveryClient will find this service as well as other services on the subnet.
        C#
        private void PopulateUserList(EndpointDiscoveryMetadata endpointDiscoveryMetadata)
        {
            if (!this.EndpointIsSelf(endpointDiscoveryMetadata.Address.Uri))
            {
                this.AddUser(new PeerUser(endpointDiscoveryMetadata.Address));
                this.EnableUsersBox();
            }
        }
        Visual Basic
        Private Sub PopulateUserList(ByVal endpointDiscoveryMetadata As EndpointDiscoveryMetadata)

            If Not Me.EndpointIsSelf(endpointDiscoveryMetadata.Address.Uri) Then
                Me.AddUser(New PeerUser(endpointDiscoveryMetadata.Address))
                Me.EnableUsersBox()
            End If
        End Sub
        Note: The PeerUser class is provided with the lab code. It is used to track chat conversations with other users.   The AddUser method adds the user to the list box.
      4. Locate the AdHocDiscovery method stub and add the following code.  This code will initiate discovery looking for services that implement the ISimpleChatService contract and then, as services are found, it will add them to the list of available services.
        C#
        private void AdHocDiscovery()
        {
            this.discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());

            this.discoveryClient.FindProgressChanged +=
                new EventHandler<FindProgressChangedEventArgs>(this.OnFindProgressChanged);
            this.discoveryClient.FindCompleted +=
                new EventHandler<FindCompletedEventArgs>(this.OnFindCompleted);

            // Setup the form for discovery
            this.ShowDiscoveryInProgress(true);

            // Do async discovery
            this.discoveryClient.FindAsync(new FindCriteria(typeof(ISimpleChatService)));
        }
        Visual Basic
        Private Sub AdHocDiscovery()
            Me.discoveryClient = New DiscoveryClient(New UdpDiscoveryEndpoint())

            ‘ Setup the form for discovery
            Me.ShowDiscoveryInProgress(True)

            ‘ Do async discovery
            Me.discoveryClient.FindAsync(New FindCriteria(GetType(ISimpleChatService)))
        End Sub
        Note: Using DiscoveryClient
        The discovery process can be started either synchronous or asynchronously by using the Find or FindAsync methods respectively. Most of the times you will use the FindAsync method (as in the preceding code).
        During discovery, you can register some events handlers. For example, FindProgressChanged is invoked each time one of service endpoints has been discovered. If you want to be notified when the discovery process is finished, then the handler FindCompleted should be used.
      5. Implement the OnFindProgressChanged and OnFindCompleted handlers as shown in the following code by pasting it below the AdHocDiscovery() method implementation.
        C#
        private void OnFindProgressChanged(object sender, FindProgressChangedEventArgs e)
        {
            this.PopulateUserList(e.EndpointDiscoveryMetadata);
        }

        private void OnFindCompleted(object sender, FindCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                this.ShowStatus("Discovery cancelled");
            }
            else if (e.Error != null)
            {
                this.discoveryClient.Close();
                MessageBox.Show(
                    e.Error.Message,
                    this.Text,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information,
                    MessageBoxDefaultButton.Button1,
                    (MessageBoxOptions)0);
            }
            else
            {
                if (this.discoveryClient.InnerChannel.State == CommunicationState.Opened)
                {
                    this.discoveryClient.Close();
                }
            }

            this.discoveryClient = null;
            this.ShowDiscoveryInProgress(false);
        }
        Visual Basic
        Private Sub OnFindProgressChanged(ByVal sender As Object,
                                              ByVal e As FindProgressChangedEventArgs) Handles discoveryClient.FindProgressChanged

            Me.PopulateUserList(e.EndpointDiscoveryMetadata)
        End Sub

        Private Sub OnFindCompleted(ByVal sender As Object,
                                    ByVal e As FindCompletedEventArgs) Handles discoveryClient.FindCompleted
            If e.Cancelled Then
                Me.ShowStatus("Discovery cancelled")
            ElseIf e.Error IsNot Nothing Then
                Me.discoveryClient.Close()
                MsgBox(e.Error.Message, MsgBoxStyle.Information, Me.Text)
            Else
                If Me.discoveryClient.InnerChannel.State = CommunicationState.Opened Then Me.discoveryClient.Close()
            End If

            Me.discoveryClient = Nothing
            Me.ShowDiscoveryInProgress(False)
        End Sub
      6. The other thing we need to do is to handle the case where the user signs out during discovery. In that case, we simply need to abort the discovery process and close down the service host. Implement the AbortDiscovery method as shown.
        C#
        private void AbortDiscovery()
        {
            if (this.discoveryClient != null)
            {
                this.discoveryClient.Close();
            }
        }
        Visual Basic
        Private Sub AbortDiscovery()
            If Me.discoveryClient IsNot Nothing Then Me.discoveryClient.Close()
        End Sub
      7. Press Ctrl+F5 to start an instance of the DiscoveryChat.exe application without debugging.
      8. Switch back to Visual Studio and press Ctrl+F5 again to launch another instance of the DiscoveryChat.exe application.
      9. Switch to one of the instances of DiscoveryChat.exe and setup the chat as follows:
        a. 
        User Name: YallaTech
        b. 
        Click Sign In
        Sign in as YallaTech
        Sign in as YallaTech
          Note: Windows Firewall may prompt you to allow the Chat client access to use the network.  It is safe to allow this.
          1. Switch to the other DiscoveryChat.exe instance and setup the chat as follows:
            a. 
            Username: Besho
            b. Click Sign In 
          2. When the Besho instance signs in, it will do ad hoc discovery immediately and locate the Fred instance.
          3. Double click on the URI in the available users list to start a chat with YallaTech:
            a. Send Message Text: Hi YallaTech
            b. Click Send
          4. Close both instances of the DiscoveryChat application.

          3 comments:

          YallaTech Facebook page