WEBSITE

Silverlight Recipes : Using Sockets to Communicate over TCP (part 2)

7/21/2012 11:34:56 AM
4.2. The Client

The Silverlight client communicates with the server program using TCP sockets. The messages exchanged by the Silverlight client and the server program are expressed as data contracts, and you use JSON as the serialization format. You further convert the JSON-formatted messages to byte arrays before you can use them with sockets.

The applicable data contracts are shown in Listing 1.

Listing 1. Data contracts to represent various messages in MessageTypes.cs
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

[DataContract]
[KnownType(typeof(ConnectionDisconnectionRequest))]
[KnownType(typeof(ConnectionReply))]
[KnownType(typeof(ConnectionDisconnectionNotification))]

[KnownType(typeof(TextMessage))]
[KnownType(typeof(ChatEndNotification))]
// a wrapper message that contains the actual message,
// facilitating easy serialization and deserialization
public class MessageWrapper
{
  [DataMember]
  public object Message { get; set; }

  //Deserialize a byte[] into a MessageWrapper
  public static MessageWrapper DeserializeMessage(byte[] Message)
  {
    MemoryStream ms = new MemoryStream(Message);
    DataContractJsonSerializer dcSer =
      new DataContractJsonSerializer(typeof(MessageWrapper));
    MessageWrapper mw = dcSer.ReadObject(ms) as MessageWrapper;
    return mw;
  }
  //serialize a MessageWrapper into a MemoryStream
  public static MemoryStream SerializeMessage(MessageWrapper Message)
  {
    MemoryStream ms = new MemoryStream();
    DataContractJsonSerializer dcSer =
      new DataContractJsonSerializer(typeof(MessageWrapper));
    dcSer.WriteObject(ms, Message);
    return ms;
  }
}

//a request from a client to the server for either a connection or a disconnection
[DataContract]
public class ConnectionDisconnectionRequest
{
  [DataMember]
  public string From { get; set; }
  [DataMember]
  public bool Connect { get; set; }

}
//a reply from the server on successful connection
[DataContract]
public class ConnectionReply
{
  [DataMember]
  public List<string> Participants;

					  

}
//a broadcast style notification to all connected clients about a
//specific client's connection/disconnection activity
[DataContract]
public class ConnectionDisconnectionNotification
{
  [DataMember]
  public string Participant { get; set; }
  [DataMember]
  public bool Connect { get; set; }
}
//a notification from a client to the server that it has ended a chat
[DataContract]
public class ChatEndNotification
{
  [DataMember]
  public string From { get; set; }
  [DataMember]
  public string To { get; set; }
}
//a chat message
[DataContract]
public class TextMessage
{
  [DataMember]
  public string From { get; set; }
  [DataMember]
  public string To { get; set; }
  [DataMember]
  public string Body { get; set; }
}

					  

You use DataContractJsonSerializer to serialize and deserialize the message types shown in Listing 1.

Because you have to deserialize from a byte[] to a CLR type on receiving a message, you face the challenge of not knowing the actual type information to pass on to DataContractJsonSerializer. To resolve this problem, you introduce a wrapper type named MessageWrapper, with a Body property of type object that contains the instance of the specific message you want to send. All messages are wrapped in this type before they are serialized to be sent out through the socket.

The KnownTypeAttributes applied to MessageWrapper ensures that the serializer uses the correct CLR type for the contained message while serializing the MessageWrapper instance to JSON, even though the Body property is of type object. It also allows you to specify typeof(MessageWrapper) as the parameter to the DataContractJsonSerializer instance for deserialization, ensuring that the correct type is used to deserialize the contained message. You define two static methods, DeserializeMessage() and SerializeMessage(), on the MessageWrapper type that encapsulates this logic.

Before you move into the sockets code, let's quickly look at the XAML UI. Listing 2 lists the XAML for MainPage.

Listing 2. XAML for the chat client page in MainPage.xaml
<UserControl x:Class="Recipe7_5.ChatClient.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="308" Height="550"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
  <UserControl.Resources>
    <ControlTemplate x:Key="ctTalkButton" TargetType="Button">
      <Grid>
        <Image Source="SpeechMicHS.png"/>
      </Grid>
    </ControlTemplate>
    <DataTemplate x:Key="dtConversation">
      <Grid Width="Auto" Height="Auto">
        <Grid.RowDefinitions>
          <RowDefinition Height="0.191*"/>
          <RowDefinition Height="0.809*"/>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding From}"
                   TextWrapping="Wrap"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Top"
                   Foreground="#FF1C2E7C"/>
        <TextBlock Text="{Binding Body}"
                   TextWrapping="Wrap"
                   HorizontalAlignment="Stretch"
                   VerticalAlignment="Top"
                   d:LayoutOverrides="VerticalAlignment"
                   Grid.Row="1"
                   Margin="8,8,8,8"
                   FontSize="12"
                   FontFamily="Georgia"
                   FontWeight="Normal"/>
      </Grid>
    </DataTemplate>
    <ControlTemplate x:Key="ct_lbxConversationItem" TargetType="ListBoxItem">
      <Grid Background="{TemplateBinding Background}">
        <Grid.RowDefinitions>
          <RowDefinition Height="*"/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

					  

<ContentPresenter
          HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
          Padding="{TemplateBinding Padding}"
          VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
          HorizontalAlignment="Stretch" Content="{TemplateBinding Content}"
          ContentTemplate="{TemplateBinding ContentTemplate}"
          TextAlignment="{TemplateBinding TextAlignment}"
          TextDecorations="{TemplateBinding TextDecorations}"
          TextWrapping="Wrap"/>
      </Grid>
    </ControlTemplate>
    <Style x:Key="style_lbxitemConversation" TargetType="ListBoxItem">
      <Setter Property="IsEnabled" Value="true"/>
      <Setter Property="Foreground" Value="#FF000000"/>
      <Setter Property="HorizontalContentAlignment" Value="Left"/>
      <Setter Property="VerticalContentAlignment" Value="Top"/>
      <Setter Property="Cursor" Value="Arrow"/>
      <Setter Property="TextAlignment" Value="Left"/>
      <Setter Property="TextWrapping" Value="Wrap"/>
      <Setter Property="FontSize" Value="12"/>
      <Setter Property="Background" Value="White"/>
      <Setter Property="Padding" Value="2,0,0,0"/>
      <Setter Property="Template"
                  Value="{StaticResource ct_lbxConversationItem}"/>
    </Style>

  </UserControl.Resources>
  <Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Border Padding="4,4,4,4" BorderBrush="Black"
            Background="LightBlue" BorderThickness="4"
            Grid.RowSpan="3"/>
    <Grid Visibility="Visible" x:Name="viewLogin" Width="300" Height="550">
      <Grid.RowDefinitions>
        <RowDefinition Height="0.364*"/>
        <RowDefinition Height="0.086*"/>
        <RowDefinition Height="0.1*"/>
        <RowDefinition Height="0.1*"/>
        <RowDefinition Height="0.35*"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>

					  

<ColumnDefinition Width="0.3*"/>
        <ColumnDefinition Width="0.43*"/>
        <ColumnDefinition Width="0.27*"/>
      </Grid.ColumnDefinitions>
      <TextBlock Text="IP" Grid.Row="0" Grid.Column="1" VerticalAlignment="Bottom"
                 HorizontalAlignment="Center" Margin="0,0,0,3" FontFamily="Arial"
                 FontSize="12" />
      <TextBlock Text="Port" Grid.Row="0" Grid.Column="2"
                 VerticalAlignment="Bottom"
                 HorizontalAlignment="Center" Margin="0,0,0,3"
                 FontFamily="Arial" FontSize="12" />
      <TextBlock Text="Server :" Grid.Row="1" Grid.Column="0"
                 VerticalAlignment="Center" HorizontalAlignment="Left"
                 Margin="0,0,0,0" Width="82" FontSize="12"
                 FontFamily="Arial" TextAlignment="Right" />
      <TextBlock Text="Your Name :" Grid.Row="2" Grid.Column="0"
                 VerticalAlignment="Center"
                 HorizontalAlignment="Left"
                 Margin="0,0,0,0"  Width="82" FontSize="12"
                 FontFamily="Arial" TextAlignment="Right" />
      <TextBox FontSize="16" x:Name="tbxIPAddress"
               Text="{Binding IP, Mode=TwoWay}"
               HorizontalContentAlignment="Center" HorizontalAlignment="Stretch"
               Grid.Row="1" Grid.Column="1"  Margin="4,0,4,0"
               VerticalAlignment="Center" TextWrapping="NoWrap"
               VerticalScrollBarVisibility="Disabled" Height="25" />
      <TextBox FontSize="16" x:Name="tbxPort" Text="{Binding Port, Mode=TwoWay}"
               HorizontalContentAlignment="Center" Width="Auto"
               HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="2"
               Margin="4,0,4,0" VerticalAlignment="Center" TextWrapping="NoWrap"
               VerticalScrollBarVisibility="Disabled" Height="25" />
      <TextBox FontSize="16" x:Name="tbxParticipantName" Text
               ="{Binding Me, Mode=TwoWay}"
               HorizontalContentAlignment="Center" Width="Auto"
               HorizontalAlignment="Stretch" Grid.Row="2" Grid.Column="1"
               Grid.ColumnSpan="2"  Margin="4,0,4,0" VerticalAlignment="Center"
               TextWrapping="NoWrap" VerticalScrollBarVisibility="Disabled"
               Height="25" />
      <HyperlinkButton FontFamily="Arial" FontSize="16"
                       HorizontalAlignment="Center" Margin="0,8,0,0"
                       x:Name="btnJoin" VerticalAlignment="Top" Grid.Row="3"
                       Grid.Column="1" Grid.ColumnSpan="1"
                       Content="Click here to join" Click="btnJoin_Click"/>
    </Grid>
    <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"

					  

Grid.Row="1" Visibility="Collapsed"
            x:Name="viewParticipants" Width="300" Height="550">
      <Grid.RowDefinitions>
        <RowDefinition Height="0.1*"/>
        <RowDefinition Height="0.9*"/>
      </Grid.RowDefinitions>
      <ListBox HorizontalAlignment="Stretch" Margin="8,8,8,8"
               VerticalAlignment="Stretch" Grid.Row="1"
               x:Name="lbxParticipants"
               ItemsSource="{Binding Participants, Mode=TwoWay}">
        <ListBox.ItemTemplate>
          <DataTemplate>
            <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.854*"/>
                <ColumnDefinition Width="0.146*"/>
              </Grid.ColumnDefinitions>
              <TextBlock FontSize="12" Text="{Binding}" TextAlignment="Left"
                             TextWrapping="Wrap" HorizontalAlignment="Stretch"
                             Margin="5,5,5,5" VerticalAlignment="Stretch"/>
              <Button Template="{StaticResource ctTalkButton}"
                          HorizontalAlignment="Right" Margin="8,8,8,8"
                          Grid.Column="1" Content="Button" Click="btnTalk_Click"
                          Tag="{Binding}"/>
            </Grid>
          </DataTemplate>
        </ListBox.ItemTemplate>
      </ListBox>
      <HyperlinkButton HorizontalAlignment="Right" VerticalAlignment="Center"
                       Content="Click to Logoff" Margin="8,8,8,8" FontSize="14"
                       x:Name="btnLogoff" Click="btnLogoff_Click" />
    </Grid>
    <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="2"
          Visibility="Collapsed" x:Name="viewChat" Width="300" Height="550">
      <Grid.RowDefinitions>
        <RowDefinition Height="0.053*"/>
        <RowDefinition Height="0.607*"/>
        <RowDefinition Height="0.284*"/>
        <RowDefinition Height="0.056*"/>
      </Grid.RowDefinitions>
      <ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
               Margin="8,8,8,8" x:Name="lbxConversation" Grid.Row="1"
               ItemTemplate="{StaticResource dtConversation}"
               ItemsSource="{Binding Conversation, Mode=TwoWay}"
               ItemContainerStyle="{StaticResource style_lbxitemConversation}"/>

					  

<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
               Text="{Binding MessageBody, Mode=TwoWay}" TextWrapping="Wrap"
               Grid.Row="2" Margin="8,8,8,8" VerticalScrollBarVisibility="Auto"
               FontFamily="Courier New" Foreground="#FF0B356A"
               x:Name="tbxMessage"/>
      <HyperlinkButton HorizontalAlignment="Center" VerticalAlignment="Center"
                       Content="Click to Send" Grid.Row="3"
                       Margin="0,0,0,0" FontSize="14" x:Name="btnSend"
                       Click="btnSend_Click"/>
      <HyperlinkButton FontSize="14" HorizontalAlignment="Right" Margin="0,0,8,8"
                       x:Name="btnEndChat" VerticalAlignment="Stretch"
                       Content="End Chat" Click="btnEndChat_Click"/>
    </Grid>
  </Grid>
</UserControl>

					  

The XAML is pretty simple. The UI is broken into three views, contained in three corresponding Grids: viewLogin, viewParticipants, and viewChat.

viewLogin exposes the login UI, made up of TextBoxes to accept the IP address, the server port, and the participant name. The fields are bound to properties on the ClientConnectionManager class, which we discuss momentarily. It also contains a HyperlinkButton, which when clicked initiates the login process.

viewParticipants contains a ListBox named lbxParticipants that displays the currently joined participants, except for the participant logged in through this client instance. lbxParticipants is bound to the ClientConnectionManager.Participants property. The data template for each item consists of a TextBlock showing the participant name, and a custom templated Button displaying an icon, which when clicked initiates a conversation with the corresponding participant. Another HyperlinkButton lets the user log off.

viewChat contains a ListBox named lbxConversation that displays the conversation history, bound to the ClientConnectionManager.Conversation property, and a TextBox that lets the user type in a message, bound to ClientConnectionManager.MessageBody. It also contains two more HyperlinkButtons to send a message and to end a chat.

viewParticipants and viewChat are initially hidden and are made visible depending on the state of the application.

Other  
  •  Microsoft ASP.NET 3.5 : The HTTP Request Context - The global.asax File
  •  Microsoft ASP.NET 3.5 : The HTTP Request Context - Initialization of the Application
  •  Free Email Everywhere and Anytime
  •  IIS 7.0 : Managing Web Sites - Administrative Tasks - Limiting Web Site Usage, Configuring Web Site Logging and Failed Request Tracing
  •  IIS 7.0 : Managing Web Sites - Administrative Tasks - Adding a New Web Site
  •  IIS 7.0 : Managing Worker Processes and Requests
  •  Websocket: Internet In Real Time
  •  IIS 7.0 : Managing Application Pools (part 3) - Advanced Application Pool Configuration, Monitoring Application Pool Recycling Events
  •  IIS 7.0 : Managing Application Pools (part 2) - Managing Application Pool Identities
  •  IIS 7.0 : Managing Application Pools (part 1) - Application Pool Considerations, Adding a New Application Pool
  •  
    Top 10
    Review : Sigma 24mm f/1.4 DG HSM Art
    Review : Canon EF11-24mm f/4L USM
    Review : Creative Sound Blaster Roar 2
    Review : Philips Fidelio M2L
    Review : Alienware 17 - Dell's Alienware laptops
    Review Smartwatch : Wellograph
    Review : Xiaomi Redmi 2
    Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
    Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
    3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    VIDEO TUTORIAL
    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
    Popular Tags
    Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone