Sdílet prostřednictvím


How to create and use a UDP socket client app for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

 

This topic introduces you to the steps necessary to create a simple UDP socket client app for Windows Phone. A typical scenario is that a client app communicates with a server over a UDP socket. For the sake of simplicity, and to keep this topic self-contained, we will use the built-in Simple TCP/IP Services on your computer as the server side of this communication. To create a similar client app using a TCP socket, see How to create and use a TCP socket client app for Windows Phone 8.

Important Note:

This topic requires the use of a UDP socket server for client communications. The Simple TCP/IP Services feature of Windows is used as the server component in this example. The following procedures provide instruction for enabling the Simple TCP/IP Services feature on your computer. You may prefer to use your own socket server implementation.

This topic contains the following sections.

 

Creating the UDP socket client UI

In this section, you create the UI for demonstrating the UDP Socket client functionality.

To create the UDP socket client UI

  1. In Visual Studio, create a new project by selecting the File | New Project menu command.

  2. The New Project window is displayed. Expand the Visual C# templates, and then select the Windows Phone templates.

  3. Select the **Windows Phone App ** template. Fill in the Name with a name of your choice.

  4. Click OK. The Windows Phone platform selection dialog box appears. Select a target version, or accept the default.

  5. Click OK. A new project is created, and MainPage.xaml is opened in the Visual Studio designer window.

  6. On MainPage.xaml, remove the XAML code for the StackPanel named “TitlePanel” and replace it with the following code:

            <!--TitlePanel contains the name of the application and page title-->
            <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
                <TextBlock x:Name="ApplicationTitle" Text="UDP Socket Application" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="Client" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    

    The XAML code adds two TextBlock elements to the app, one for the app title named “ApplicationTitle” and one for the page title named “PageTitle”.

  7. On MainPage.xaml, remove the XAML code for the Grid named “ContentPanel” and replace it with the following code:

           <!--ContentPanel - place additional content here-->
            <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,-8,12,8">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>    <!-- Fit to content -->
                    <ColumnDefinition Width="Auto"/>    <!-- Fit to content -->
                    <ColumnDefinition Width="Auto"/>    <!-- Fit to content -->
                    <ColumnDefinition Width="*"/>       <!-- Take up remaining space -->
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>      <!-- Fit to content -->
                    <RowDefinition Height="Auto"/>      <!-- Fit to content -->
                    <RowDefinition Height="Auto"/>      <!-- Fit to content -->
                    <RowDefinition Height="*"/>         <!-- Take up remaining space -->
                </Grid.RowDefinitions>
    
                <!-- Grid Row 0: Remote Host Input Field >-->
                <TextBlock Grid.Row="0" Grid.Column="0" Text="Host Name:"
                            VerticalAlignment="Center" HorizontalAlignment="Center"
                            FontSize="{StaticResource PhoneFontSizeNormal}" />
                <TextBox x:Name="txtRemoteHost" Grid.Row="0" Grid.Column="1"  Height="70" Width="200"
                          VerticalAlignment="Top" HorizontalAlignment="Left"
                          FontSize="{StaticResource PhoneFontSizeNormal}"  />
    
                <!-- Grid Row 1: Echo >-->
                <!-- TextBlock for Echo command label-->
                <TextBlock Grid.Row="1" Grid.Column="0" Text="Text To Echo:"
                            VerticalAlignment="Center" HorizontalAlignment="Center"
                            FontSize="{StaticResource PhoneFontSizeNormal}" />
    
                <!-- TextBox for Echo command text input-->
                <TextBox x:Name="txtInput" Grid.Row="1" Grid.Column="1" Height="70" Width="200"
                          VerticalAlignment="Top" HorizontalAlignment="Left"
                          FontSize="{StaticResource PhoneFontSizeNormal}" />
    
                <!-- Button to the right of the input textbox for the Echo command >-->
                <Button x:Name="btnEcho" Grid.Row="1" Grid.Column="2" Height="70"  Width="120"
                         Content="Echo"
                         FontSize="{StaticResource PhoneFontSizeNormal}" Click="btnEcho_Click"/>
    
                <!-- Grid Row 2: Quote of the Day-->
                <!-- Button for the Quote command >-->
                <Button x:Name="btnGetQuote" Grid.Row="2" Grid.ColumnSpan="4" Height="70"
                         Content="Get Quote of the Day"
                         FontSize="{StaticResource PhoneFontSizeNormal}" Click="btnGetQuote_Click"/>
    
                <!-- Grid Row 3: Output-->
                <!-- Output TextBox named 'txtOutput' >-->
                <TextBox x:Name="txtOutput" Grid.Row="3" Grid.ColumnSpan="4" Background="Black" BorderBrush="Green"
                          AcceptsReturn="False" Foreground="LightGray" FontFamily="Courier New"
                          IsHitTestVisible="False" FontSize="{StaticResource PhoneFontSizeSmall}" TextWrapping="Wrap" />
            </Grid>
    

    The XAML code creates a Grid element to contain all other elements. It first defines the Grid to have three rows and three columns. Next, the XAML defines a TextBox for the data input of the remote host name and a TextBlock as a label for this field. The next row of the Grid consists of another TextBox for more data input from the user, and two Button elements with their Click event handlers assigned. Finally, the XAML defines another TextBox to display the output from the app. You implement the btnEcho_Click and btnGetQuote_Click methods in the following sections. The layout in the designer should look like this.

Connecting to a UDP socket server

In this section, you create a socket and connect to the server by using the System.Net.Sockets API. The calls to the System.Net.Sockets API are encapsulated in a SocketClient class for clarity.

To connect to a UDP socket server

  1. Create a new class by selecting the project in the Solution Explorer window, right-clicking it and selecting Add | Class … from the context menu. This brings up the Add New Item dialog with the class template selected. Change the name of the class in the Name field to “SocketClient” and click Add. A new class with the name “SocketClient” is created in the project.

  2. Open SocketClient.cs and add the following using directives to the top of the page:

    using System.Net.Sockets;using System.Threading;using System.Text;
    
  3. Define the following variables at the top of the SocketClient class:

            // Cached Socket object that will be used by each call for the lifetime of this class
            Socket _socket = null;
            // Signaling object used to notify when an asynchronous operation is completed
            static ManualResetEvent _clientDone = new ManualResetEvent(false);
            // Define a timeout in milliseconds for each asynchronous call. If a response is not received within this
            // timeout period, the call is aborted.
            const int TIMEOUT_MILLISECONDS = 5000;
            // The maximum size of the data buffer to use with the asynchronous socket methods
            const int MAX_BUFFER_SIZE = 2048;
    

    The _socket variable is used to store the Socket object after it is created. The _clientDone variable is a ManualResetEvent that is used to coordinate the asynchronous calls that are invoked through the System.Net.Sockets API.

  4. In SocketClient.cs, add the following constructor, which creates the UDP socket.

            /// <summary>
            /// SocketClient Constructor
            /// </summary>
            public SocketClient()
            {
                // The following creates a socket with the following properties:
                // AddressFamily.InterNetwork - the socket will use the IP version 4 addressing scheme to resolve an address
                // SocketType.Dgram - a socket that supports datagram (message) packets
                // PrototcolType.Udp - the User Datagram Protocol (UDP)
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            }
    
Important Note:

AddressFamily..::.InterNetwork is the only valid value for the AddressFamily parameter in the Socket constructor in Windows Phone OS 7.1. Selecting any other member of the AddressFamily enumeration will result in an error.

Sending data to a UDP socket server

In this section, you add a Send method to the SocketClient class in order to send data to the server.

To send data to a UDP socket server

  • In SocketClient.cs, add the following method.

           /// <summary>
            /// Send the given data to the server using the established connection
            /// </summary>
            /// <param name="serverName">The name of the server</param>
            /// <param name="portNumber">The number of the port over which to send the data</param>
            /// <param name="data">The data to send to the server</param>
            /// <returns>The result of the Send request</returns>
            public string Send(string serverName, int portNumber, string data)
            {
                string response = "Operation Timeout";
                // We are re-using the _socket object that was initialized in the Connect method
                if (_socket != null)
                {
                    // Create SocketAsyncEventArgs context object
                    SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
                    // Set properties on context object
                    socketEventArg.RemoteEndPoint = new DnsEndPoint(serverName, portNumber);
                    // Inline event handler for the Completed event.
                    // Note: This event handler was implemented inline in order to make this method self-contained.
                    socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                    {
                        response = e.SocketError.ToString();
                        // Unblock the UI thread
                        _clientDone.Set();
                    });
                    // Add the data to be sent into the buffer
                    byte[] payload = Encoding.UTF8.GetBytes(data);
                    socketEventArg.SetBuffer(payload, 0, payload.Length);
                    // Sets the state of the event to nonsignaled, causing threads to block
                    _clientDone.Reset();
                    // Make an asynchronous Send request over the socket
                    _socket.SendToAsync(socketEventArg);
                    // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
                    // If no response comes back within this time then proceed
                    _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
                }
                else
                {
                    response = "Socket is not initialized";
                }
                return response;
            }
    

    This method illustrates the following concepts:

    • Create a context object: This object is the context object for whatever asynchronous method is being called (in this case SendToAsync). The data buffer, callback method, and various other context-specific data is set on this object and then passed to the asynchronous call. Once the call is complete, this object can be examined for completion status and operation result.

    • Set the RemoteEndpoint on the context object: This property sets the remote endpoint to which to send the data. In this example, it is defined as a DnsEndPoint constructed with the serverName and portNumber parameters.

    • Set the buffer on the context object: The data to be sent to the server is placed in the buffer of the SocketAsyncEventArgs object as an array of bytes.

    • Define a callback for the Completed event: This method uses a delegate in order to handle the Completed event from the SendToAsync call.

    • Wait until the call is complete: In this example, the WaitOne method causes the UI Thread to block until the Completed event fires or the call times out.

Receiving data from a UDP socket server

In this section, you add a Receive method to the SocketClient class in order to receive data from the server.

To receive data from a UDP socket server

  • In SocketClient.cs, add the following code.

            /// <summary>
            /// Receive data from the server
            /// </summary>
            /// <param name="portNumber">The port on which to receive data</param>
            /// <returns>The data received from the server</returns>
            public string Receive(int portNumber)
            {
                string response = "Operation Timeout";
                // We are receiving over an established socket connection
                if (_socket != null)
                {
                    // Create SocketAsyncEventArgs context object
                    SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
                    socketEventArg.RemoteEndPoint = new IPEndPoint(IPAddress.Any, portNumber);
                    // Setup the buffer to receive the data
                    socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE);
                    // Inline event handler for the Completed event.
                    // Note: This even handler was implemented inline in order to make this method self-contained.
                    socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                    {
                        if (e.SocketError == SocketError.Success)
                        {
                            // Retrieve the data from the buffer
                            response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                            response = response.Trim('\0');
                        }
                        else
                        {
                            response = e.SocketError.ToString();
                        }
                        _clientDone.Set();
                    });
                    // Sets the state of the event to nonsignaled, causing threads to block
                    _clientDone.Reset();
                    // Make an asynchronous Receive request over the socket
                    _socket.ReceiveFromAsync(socketEventArg);
                    // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
                    // If no response comes back within this time then proceed
                    _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
                }
                else
                {
                    response = "Socket is not initialized";
                }
                return response;
            }
    
            /// <summary>
            /// Closes the Socket connection and releases all associated resources
            /// </summary>
            public void Close()
            {
                if (_socket != null)
                {
                    _socket.Close();
                }
            }
    

    This method illustrates the following concepts:

    • Create a context object: This object is the context object for whatever asynchronous method is being called (in this case, ReceiveFromAsync). The data buffer, callback method, and various other context-specific data is set on this object and then passed to the asynchronous call. Once the call is complete, this object can be examined for completion status and operation result.

    • **Set the RemoteEndpoint on the context object:**This property sets the remote endpoint from which to receive data. In this example, it is defined as an IPEndPoint using IPAddress..::.Any to specify that the client should listen on all network interfaces and setting the port number to the portNumber parameter.

    • Set the buffer on the context object: The data to be received from the server is placed in the buffer of the SocketAsyncEventArgs object as an array of bytes.

    • Define a callback for the Completed event: This method uses a delegate to handle the Completed event from the ReceiveFromAsync call.

    • Wait until the call is complete: In this example, the Connect method blocks until the Completed event fires or the call times out.

    • Close method: For the client app to explicitly close the Socket that was created, a method Close is added to the SocketClient class.

Using the SocketClient class in the app

In the previous sections a SocketClient class was implemented to encapsulate all the operations necessary to communicate with the server over a UDP socket. Now we return to the client app and add the functionality necessary to use this SocketClient class. This app communicates with the Echo service and the Quote of the Day (QOTD) service on your computer. The Echo service echoes back whatever data was sent to it. The Quote of the Day service returns a quotation as one or more lines of text in a message. We add the event handlers for the Click events from the btnEcho and btnGetQuote buttons. We also add some methods to perform output to the txtOutputTextBox and to validate input from the txtRemoteHost and txtInputTextBox elements.

To use the SocketClient class in the app

  1. At the top of the MainPage.xaml.cs class, define the following constants:

           // Constants
            const int ECHO_PORT = 7;  // The Echo protocol uses port 7 in this sample
            const int QOTD_PORT = 17; // The Quote of the Day (QOTD) protocol uses port 17 in this sample
    
  2. In MainPage.xaml.cs, add the following code.

           /// <summary>
            /// Handle the btnEcho_Click event by sending text to the echo server and outputting the response
            /// </summary>
            private void btnEcho_Click(object sender, RoutedEventArgs e)
            {
                // Clear the log
                 ClearLog();
                // Make sure we can perform this action with valid data
                if (ValidateRemoteHost() && ValidateInput())
                {
                    // Instantiate the SocketClient
                    SocketClient client = new SocketClient();
                                    // Attempt to send our message to be echoed to the echo server
                    Log(String.Format("Sending '{0}' to server ...", txtInput.Text), true);
                    string result = client.Send(txtRemoteHost.Text, ECHO_PORT,txtInput.Text);
                    Log(result, false);
                    // Receive a response from the echo server
                    Log("Requesting Receive ...", true);
                    result = client.Receive(ECHO_PORT);
                    Log(result, false);
                    // Close the socket connection explicitly
                    client.Close();
                }
            }
    
            /// <summary>
            /// Handle the btnGetQuote_Click event by receiving text from the Quote of the Day (QOTD) server and outputting the response
            /// </summary>
            private void btnGetQuote_Click(object sender, RoutedEventArgs e)
            {
                // Clear the log
                 ClearLog();
                // Make sure we can perform this action with valid data
                if (ValidateRemoteHost())
                {
                    // Instantiate the SocketClient object
                    SocketClient client = new SocketClient();
                    // Quote of the Day (QOTD) sends a short message (selected by the server’s administrator) to a client device.
                     // For UDP, the message is sent for each incoming UDP message, so here we send a "dummy" message to solicit
                     // a response. The message cannot be empty, so the message below consists of one whitespace.
                    Log(String.Format("Requesting a quote from server '{0}' ...", txtInput.Text), true);
                    string dummyMessage = " ";
                    string result = client.Send(txtRemoteHost.Text, QOTD_PORT, dummyMessage);
                    Log(result, false);
                    // Receive response from the QOTD server
                    Log("Requesting Receive ...", true);
                    result = client.Receive(QOTD_PORT);
                    Log(result, false);
                    // Close the socket connection explicitly
                    client.Close();
                }
            }
    
            #region UI Validation
            /// <summary>
            /// Validates the txtInput TextBox
            /// </summary>
            /// <returns>True if the txtInput TextBox contains valid data, False otherwise</returns>
            private bool ValidateInput()
            {
                // txtInput must contain some text
                if (String.IsNullOrWhiteSpace(txtInput.Text))
                {
                    MessageBox.Show("Please enter some text to echo");
                    return false;
                }
                return true;
            }
    
            /// <summary>
            /// Validates the txtRemoteHost TextBox
            /// </summary>
            /// <returns>True if the txtRemoteHost contains valid data, False otherwise</returns>
            private bool ValidateRemoteHost()
            {
                // The txtRemoteHost must contain some text
                if (String.IsNullOrWhiteSpace(txtRemoteHost.Text))
                {
                    MessageBox.Show("Please enter a host name");
                    return false;
                }
                return true;
            }
            #endregion
    
            #region Logging
            /// <summary>
            /// Log text to the txtOutput TextBox
            /// </summary>
            /// <param name="message">The message to write to the txtOutput TextBox</param>
            /// <param name="isOutgoing">True if the message is an outgoing (client to server) message, False otherwise</param>
            /// <remarks>We differentiate between a message from the client and server
             /// by prepending each line  with ">>" and "<<" respectively.</remarks>
            private void Log(string message, bool isOutgoing)
            {
                string direction = (isOutgoing) ? ">> " : "<< ";
                txtOutput.Text += Environment.NewLine + direction + message;
            }
    
            /// <summary>
            /// Clears the txtOutput TextBox
            /// </summary>
            private void ClearLog()
            {
                txtOutput.Text = String.Empty;
            }
            #endregion
    

    The Echo operation is implemented in the above code, as follows:

    • Handle the Click event on the btnEcho button: The Echo operation is implemented in the btnEcho_Click event. Since a UDP is connectionless, a connection does not need to be established before sending data to the server. The Echo operation over UDP consists of a Send and Receive operation on the socket.

    • SocketClient object is scoped to the method: This was done for the sake of simplicity and to keep the Echo and GetQuote calls self-contained. An instance of the SocketClient class could also be stored at the class scope and then reused.

    • Port numbers as constants: The client connects to the Echo Protocol in this sample using the well-known port number 7. This port number is defined as ECHO_PORT in the MainPage.xaml.cs class.

    • Connect: This is done through a call to the Connect method of SocketClient, passing in the hostname received in txtRemoteHost and the port number defined in the ECHO_PORT constant. The input is validated and feedback from this operation is written to txtOutput by using the Log method.

    • Send: The text in the txtInput field is sent to the server by using the Send method on the SocketClient object. The input is validated and feedback from this operation is written to txtOutput by using the Log method.

    • Receive: Next, the data from the echo server is received by calling the Receive method on the SocketClient object. This is written to the txtOutput by using the Log method.

    • Close: Finally, the socket is closed by explicitly calling the Close method of the SocketClient object.

    The GetQuote operation is implemented in the preceding code as follows:

    • Handle the Click event on the btnGetQuote button: The GetQuote operation is implemented in the btnGetQuote_Click event. Since a UDP is connectionless, a connection does not need to be established before sending data to the server. The GetQuote operation over UDP consists of a Send and Receive operation on the socket. The send is required because QOTD over UDP sends a quote message for each incoming message it receives. In the preceding example, we use a dummy message to send to the QOTD services to initiate a response.

    • SocketClient object is scoped to the method: This was done for the sake of simplicity and to keep the Echo and GetQuote calls self-contained. An instance of the SocketClient class could also be stored at the class scope and then reused.

    • Port numbers as constants: The client connects to the Quote of the Day (QOTD) Protocol in this sample using the well-known port number 17. This port number is defined as QOTD_PORT in the MainPage.xaml.cs class.

    • Connect: This is done through a call to the Connect method of SocketClient, passing in the hostname received in txtRemoteHost and the port number defined in the QOTD_PORT constant. The input is validated, and feedback from this operation is written to the txtOutput by using the Log method.

    • Send: There is no send request made in the case of Quote of the Day. We simply pull the quote from the server by using a Receive request.

    • Receive: Next, the data from the quote of the day server is received by calling the Receive method on the SocketClient object. This is written to txtOutput by using the Log method.

    • Close: Finally, the socket is closed by explicitly calling the Close method of the SocketClient object.

Enabling Simple TCP/IP Services on your computer

This topic uses the Echo and Quote of the Day services available on every Windows computer. Simple TCP/IP Services is a feature available on all versions of Windows. This feature offers the following services: Character Generator, Daytime, Discard, Echo, and Quote of the Day. Each service is accessible over UDP and each is assigned a default port over which to communicate. The default service to port mapping is as follows.

Service Name

Description

Port

Echo

Echoes back data from any messages it receives on this server port. Echo can be useful as a network debugging and monitoring tool.

7

Quote of the Day

Returns a quotation as one or more lines of text in a message. Quotations are taken at random from the following file: %SYSTEMROOT%\System32\Drivers\Etc\Quotes. A sample quote file is installed with Simple TCP/IP Services. If this file is missing, the quote service fails.

17

Daytime

Returns messages containing the day of the week, month, day, year, current time (in hh:mm:ss format), and time zone information. Some programs can use the output from this service for debugging or monitoring variations in system clock time or on a different host.

13

Character Generator

Sends data made up of the set of 95 printable ASCII characters. Useful as a debugging tool for testing or troubleshooting line printers.

19

Discard

Discards all messages received on this port without response or acknowledgment. Can serve as a null port for receiving and routing TCP/IP test messages during network setup and configuration or, in some cases, can be used by programs as a message discard function.

9

To enable Simple TCP/IP Services on your computer

  1. In Control Panel, open Programs and Features.

  2. Click Turn Windows Features On or Off.

  3. In the Windows Features dialog, select the Simple TCP/IP Services check box to enable this feature, and then click OK.

Important Note:

To perform this procedure, you must be a member of the Administrators group or the Network Configuration Operators group on the local computer.

  1. In the Services list on your computer, verify that the Simple TCP/IP Services service is started. If not, start the service manually. For more information about starting a service, see Configure How a Service Is Started.

Running the UDP socket client app

This section describes how to run the app that was produced in this topic.

To run the UDP socket client app

  1. On a device, run the app by selecting the Debug | Start Debugging menu command.

  2. To try the Echo feature:

    1. Add a host name in the Host Name field.

    2. Add the text that you want to send in the Text to Echo field.

    3. Click the Echo button.

    In the output window, you should see the communication round-trips between the client on the phone and the server, including any errors that may have occurred.

  3. To try the Quote of the Day feature:

    1. Add a host name in the Host Name field.

    2. Click the Get Quote button.

    In the output window, you should see the communication round-trips between the client on the phone and the server, including any errors that may have occurred.

See Also

Reference

System.Net.Sockets

Microsoft.Phone.Net.NetworkInformation

Other Resources

How to create and use a TCP socket client app for Windows Phone 8

How to send and receive data in a multicast group for Windows Phone 8

Sockets for Windows Phone 8

Network and network interface information for Windows Phone 8