Building TCP/IP Applications on the Windows Phone Platform

Introduction

Windows Phone 8 supports different means of communication – sockets, Bluetooth and proximity. In fact, chat-like applications are readily supported using sockets that reside in the System.Net.Sockets namespace.

Sockets can be used for a variety of communication modes – FTP, email, chats, and streaming multimedia. With sockets, Windows Phone applications can create rich client applications built over TCP or UDP.

Basics of TCP Communication Using Sockets in Windows Phone

1. The client needs to declare the endpoint on which it will communicate as part of the connection request (which is an asynchronous operation).

2. After a connection is established, the client can send data by setting up a buffer of data.

3. The client can also receive data via asynchronous call.

4. Once communication is no longer needed, the client disconnects the socket and closes the communication channel.

Hands On

Create a new Windows Phone project called WPTCPDemo.

New Windows Phone Project
New Windows Phone Project

Next, add a few controls as shown below – textblocks and a button.

Add Controls
Add Controls

The controls that are added are shown in the highlighted code below.

<phone:PhoneApplicationPage
    x:Class="WPTCPDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">
 
    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <!-- LOCALIZATION NOTE:
            To localize the displayed strings copy their values to appropriately named
            keys in the app's neutral language resource file (AppResources.resx) then
            replace the hard-coded text value between the attributes' quotation marks
            with the binding clause whose path points to that string name.
 
            For example:
 
                Text="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}"
 
            This binding points to the template's string resource named "ApplicationTitle".
 
            Adding supported languages in the Project Properties tab will create a
            new resx file per language that can carry the translated values of your
            UI strings. The binding in these examples will cause the value of the
            attributes to be drawn from the .resx file that matches the
            CurrentUICulture of the app at run time.
         -->
 
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Button x:Name="buttonGetTimeFromServer" Content="Get Time from Server" HorizontalAlignment="Left" Margin="99,229,0,0" VerticalAlignment="Top" Click="buttonGetTimeFromServer_Click"/>
            <TextBlock x:Name="textBlockTimeFromServer" HorizontalAlignment="Left" Margin="76,340,0,0" TextWrapping="Wrap" Text="No data received" VerticalAlignment="Top" Width="294" Height="57"/>
            <TextBox x:Name="textBoxServerName" HorizontalAlignment="Left" Height="72" Margin="99,87,0,0" TextWrapping="Wrap" Text="Enter Server Name" VerticalAlignment="Top" Width="305"/>
            <TextBlock HorizontalAlignment="Left" Margin="17,112,0,0" TextWrapping="Wrap" Text="Server" VerticalAlignment="Top" RenderTransformOrigin="-2.157,-2.971"/>
 
        </Grid>
 
        <!--Uncomment to see an alignment grid to help ensure your controls are
            aligned on common boundaries.  The image has a top margin of -32px to
            account for the System Tray. Set this to 0 (or remove the margin altogether)
            if the System Tray is hidden.
 
            Before shipping remove this XAML and the image itself.-->
        <!--<Image Source="/Assets/AlignmentGrid.png" VerticalAlignment="Top" Height="800" Width="480" Margin="0,-32,0,0" Grid.Row="0" Grid.RowSpan="2" IsHitTestVisible="False" />-->
    </Grid>
 
</phone:PhoneApplicationPage>

Next, we will add the code that will implement support for sockets in our application.

Open up the code behind for MainPage.xaml (MainPage.xaml.cs).

Include the following namespaces in the “using” block.

using System.Net.Sockets;
using System.Threading;
using System.Text;

Next, we declare objects of Socket class and ManualResetEvent class as variables of MainPage class.

public partial class MainPage : PhoneApplicationPage
    {
        Socket mySocket = null;
        static ManualResetEvent operationComplete = new ManualResetEvent(false);
        // Constructor
        public MainPage()

Next, we will implement the function to instantiate the socket and connect it to the remote endpoint.

public void Connect(string hostname)
        {
            if (mySocket == null)
            {
                //TODO Wrap this under a try-catch block since the connect operation can fail for numerous reasons.
                mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                SocketAsyncEventArgs mySocketEventArg = new SocketAsyncEventArgs();
                // We are hardcoding the port number to 13.
                mySocketEventArg.RemoteEndPoint = new DnsEndPoint(hostname, 13);
 
               
mySocketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
 
                    operationComplete.Set();
                });
 
                operationComplete.Reset();
                mySocket.ConnectAsync(mySocketEventArg);
                operationComplete.WaitOne(5000);
            }
        }

In the above snippet, note that we have hard-coded the port to 13 (because of the configuration we will be using to test this) and the timeout to 5 seconds.

After the connection is established, we need to implement a method to send the data.

public void SendData(string data)
        {
            if (mySocket == null)
                this.Connect(textBoxServerName.Text);
 
            SocketAsyncEventArgs mySocketEventArg = new SocketAsyncEventArgs();
 
           
mySocketEventArg.RemoteEndPoint = mySocket.RemoteEndPoint;
            mySocketEventArg.UserToken = null;
 
           
mySocketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
            {
                operationComplete.Set();
            });
 
            byte[] payload = Encoding.UTF8.GetBytes(data);
            mySocketEventArg.SetBuffer(payload, 0, payload.Length);
 
            operationComplete.Reset();
 
            mySocket.SendAsync(mySocketEventArg);
           
            operationComplete.WaitOne(5000);
        }

Similarly, we need to implement a method to receive data.

public string ReceiveData()
        {
            string receivedData = null;
            if (mySocket != null)
            {
                SocketAsyncEventArgs mySocketEventArg = new SocketAsyncEventArgs();
                mySocketEventArg.RemoteEndPoint = mySocket.RemoteEndPoint;
 
                // Setup the buffer to receive the data (hardcoding to 2048 bytes)
                mySocketEventArg.SetBuffer(new Byte[2048], 0, 2048);
 
                mySocketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
                    if (e.SocketError == SocketError.Success)
                    {
                        receivedData = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                    }
                    else
                    {
                        receivedData = e.SocketError.ToString();
                    }
 
                    operationComplete.Set();
                });
 
                operationComplete.Reset();
 
                mySocket.ReceiveAsync(mySocketEventArg);
 
                operationComplete.WaitOne(5000);
            }
            return receivedData;
        }

Finally, we will implement the method that the user will invoke to communicate with the remote endpoint over sockets.

private void buttonGetTimeFromServer_Click(object sender, RoutedEventArgs e)
        {
            this.Connect(textBoxServerName.Text);
            this.SendData("");
            textBlockTimeFromServer.Text = this.ReceiveData();
            this.mySocket.Close();
            this.mySocket.Dispose();
            this.mySocket = null;
        

Our application is now ready to test.

Testing

To ensure that we can test this easily, we will use the Simple TCP services, which are part of the Windows operating system and can be enabled using the “Turn Windows Features On/Off”.

Simple TCPIP Services
Simple TCPIP Services

You will need to restart your machine to ensure that the service is installed and started.

One of the issues of working with the Windows Phone emulator is that it has its own IP which is separate from the IP of the host operating system. To circumvent that, we need to do a couple of things.

First, detect the IP of your host operating system, and make your server port accessible to everyone.

In my case, my host operating system has an IP of 192.168.0.3 and port 13 is the port of Simple TCP/IP service, which returns the date. This command needs to be run from an elevated command prompt.

Netsh http add urlacl url=http://192.168.0.3:13/ user=everyone

Second, we need to add a firewall rule that allows TCP protocol on port 13 accessible remotely from any client on the subnet. This also needs to be run from an elevated command prompt.

Netsh advfirewall firewall add rule dir=in action=allow protocol=tcp localport=13 remoteip=localsubnet profile=private,domain name=WINPHONETCPDEMO

Where, WINPHONETCPDEMO is the name of the rule.

Now, we are ready to test our application.

Now, when we run the application and pass in the IP of our host operating system and click “Get Time from Server”, we will open a socket and communicate over TCP and get the date as a response over TCP, which is displayed on the page.

Summary

In this article, we learned how to use sockets for TCP communication in Windows Phone application.

I hope you have found this information useful.

About the author

Vipul Patel is a Program Manager currently working at Amazon Corporation. He has formerly worked at Microsoft in the Lync team and in the .NET team (in the Base Class libraries and the Debugging and Profiling team). He can be reached at vipul.patel@hotmail.com

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read