Condividi tramite


How to connect with a stream socket (XAML)

This topic shows how to enable a Windows Runtime app to send and receive data with a TCP stream socket using a StreamSocket. A TCP socket provides low-level network data transfers in either direction for long-lived connections. TCP sockets are the underlying feature used by most of the network protocols used on the Internet.

The sample creates a TCP socket to make a network connection, uses the socket to send and receive data, and then closes the socket.

The sample demonstrates the following features:

  • Use the StreamSocket class to create a TCP socket.
  • Make a network connection to a TCP network server using one of the StreamSocket.ConnectAsync methods.
  • Send data to the server using the Streams.DataWriter object which allows a programmer to write common types (integers and strings, for example) on a stream.
  • Receive data from the server using the Streams.DataReader object which allows a programmer to read common types (integers and strings, for example) on a stream.
  • Close the StreamSocket.

Prerequisites

The following examples use C# or C++ and are loosely based on the StreamSocket sample. For general help creating a Windows Runtime app using C# or Visual Basic, see Create your first Windows Runtime app using C# or Visual Basic. For general help creating a Windows Runtime app using C++, see Create your first Windows Runtime app using C++.

To ensure your Windows Runtime app is network ready, you must set any network capabilities that are needed in the project Package.appxmanifest file. For more information, see How to set network capabilities.

Create a new project

  1. Open Microsoft Visual Studio 2013 and select New Project from the File menu.
  2. In the list of templates, choose Visual C# or Visual C++ (depending on your preferred development language).
  3. Under the section, choose Store apps.
  4. Under the section, select Universal Apps, Windows apps, or Windows Phone apps (depending on the platform you are targeting), and then select Blank Application.
  5. Name the application StreamSocketBasic and click OK.

Set capabilities to enable network access

You need to set network capabilities for your app to enable access to the local network and the Internet. For an app using a StreamSocket to connect to a network service on a different computer, the app would need network capabilities set. If the app needs to be able to connect as a client to remote services on the Internet, then the Internet (Client) capability is needed. If the app needs to be able to connect as a client to remote services on a home network or work network, then the Private Networks (Client & Server) capability is needed.

Note  On Windows Phone, there is only one network capability (Internet (Client & Server))which enables all network access for the app.

 

For more information on network access, see How to configure network isolation capabilities.

The steps below show how to set network capabilities.

  1. Use Microsoft Visual Studio to open the package.appxmanifest file.

  2. Select the Capabilities tab.

  3. To build the Windows version of the sample, Select the Internet (Client) and Private Networks (Client & Server) capabilities.

    To build the Windows Phone version of the sample, select the Internet (Client & Server) capability.

  4. Save and close the manifest file.

Add XAML UI

  • In this section, we define the app layout in XAML to specify the size and position of each object in the app. We complete the UI for the app by adding controls and content to display data.

    This sample uses simple XAML UI elements that include the following:

    • A horizontal StackPanel that contains a TextBlock for a label, a TextBox to input the server hostname or IP address, a TextBox to input the service name or TCP port number to connect to, and a Button used to start the asynchronous connection request.
    • A horizontal StackPanel that contains a TextBlock for a label, a TextBox to input text data to send to the server, and a Button used to start the asynchronous request to send the data.
    • A horizontal StackPanel that contains a TextBlock for a label and a TextBox for the current status. This is where status and error messages will be displayed.
    • A Grid where any output received from the network service is displayed. In this sample, any text data received is displayed as plain text.

    For a Visual C# sample, open the cs folder. For a Visual C++ sample, open the cpp folder. Open the existing blankPage.xaml file and rename the file to MainPage.xaml. Add the following UI elements to this file.

    Note  The XAML code below will need to be changed for a Windows Phone Store app.

     

    <Page
        x:Class="StreamSocketBasic.BlankPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:StreamSocketBasic"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
            <StackPanel Orientation="Vertical">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Hostname:" FontSize="16" Height="20"  Margin="15,0,0,0" />
                    <TextBox x:Name="ServerHostname" Text="www.contoso.com" FontSize="16" Height="20" Margin="15,0,0,0" />
                    <TextBlock Text="Port:" FontSize="16" Height="20"  Margin="15,0,0,0" />
                    <TextBox x:Name="ServerPort" Text="http" FontSize="16" Height="20" Margin="15,0,0,0" />
                    <Button Content="Connect" Click="Connect_Click" Margin="220,0,0,0" />
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Text to send:" FontSize="16" Height="20"  Margin="15,0,0,0" />
                    <TextBox x:Name="SendText" Text="hello" FontSize="16" Height="20" Margin="15,0,0,0" />
                    <Button Content="Send" Click="Send_Click" Margin="220,0,0,0" />
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Status:" FontSize="16" Height="20" Margin="15,0,0,0" />
                    <TextBox x:Name="StatusText" Text="Idle" FontSize="16" Height="Auto" TextWrapping="Wrap" Margin="15,0,0,0" />
                </StackPanel>
                <Grid 
                    Grid.Column="1" Margin="15,0,0,0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <TextBlock x:Name="OutputView" FontSize="16" Height="Auto" Margin="15,0,0,0" TextWrapping="Wrap" />
                </Grid>    
            </StackPanel>
         </Grid>
    </Page>
    

Create the StreamSocket and connect to the server

The code in this step creates some variables including the client socket and variables that track status. Variables are created to track whether the client socket is in a connected or closing state. This step also defines the hostname and service name (TCP port) to connect to on the server. The value of the hostname and service name are set to a default value which can be changed in the UI.

  1. First create the StreamSocket object.

    Open the cs folder. Open the MainPage.cs file and add the following code to the file.

    using Windows.Networking;
    using Windows.Networking.Sockets;
    
    
        private StreamSocket clientSocket;
        private HostName serverHost;
        private string serverHostnameString;
        private string serverPort;
        private bool connected = false;
        private bool closing = false;
    
        public BlankPage()
        {
            this.InitializeComponent();
            clientSocket = new StreamSocket();
        }
    
  2. Try to connect to the server.

    Most of the work is done in the click handler for the Connect Button. When this button is clicked, the text in the StatusText and OutputView UI elements are updated. The server hostname and service name (port) are used to try and establish a connection to the server and await the response. If an error or exception occurs, the result is displayed in the StatusText UI element. If a connection is established and no errors occur, the StatusText is updated.

    An data received from the network server is displayed in the OutputView UI element.

    When you use the await keyword in C# and Visual Basic, you must also define the method as asynchronous. Using the await keyword in C# and Visual Basic, the code for making the connection asynchronously is similar to the code we would use to complete this operation synchronously.

    The ConnectAsync(HostName, String) method throws an exception if a TCP connection could not be established with the network server. The sample code uses a try/catch block to catch an exception and print out the exception message in the StatusText UI element if an error occurs. In the try block, update the status.

            private async void Connect_Click(object sender, RoutedEventArgs e)
            {
                if (connected) {
                   StatusText.Text = "Already connected";
                   return;
                }
    
                try
                {
                    OutputView.Text = "";
                    StatusText.Text = "Trying to connect ...";
    
                    serverHost = new HostName(serverHostname.Text);
                    // Try to connect to the 
                    await clientSocket.ConnectAsync(serverHost, serverPort.Text);
                    connected = true;
                    StatusText.Text = "Connection established"+Environment.NewLine;
    
                }
                catch (Exception exception) {
                    // If this is an unknown status, 
                    // it means that the error is fatal and retry will likely fail.
                    if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
                        throw;
                     }
    
                    StatusText.Text = "Connect failed with error: " + exception.Message;
                    // Could retry the connection, but for this simple example
                    // just close the socket.
    
                    closing = true;
                    // the Close method is mapped to the C# Dispose
                    clientSocket.Dispose();
                    clientSocket = null;
    
                }
            }
    

Sent data and receive responses from the server

The code in this step sends data to the server and receives responses from the server.

  • Most of the work is done in the click handler for the Send Button. When this button is clicked, the text in the StatusText and OutputView UI elements are updated, then the app tries to send the text data entered in the Sendtext UI element to the server and receive any response. If an error or exception occurs, the result is displayed in the StatusText UI element. If data is sent and no errors occur, the StatusText is updated.

    An data received from the network server is displayed in the OutputView UI element.

    Using the await keyword in C# and Visual Basic, the code for sending the data and retrieving the response asynchronously is similar to the code we would use to complete this operation synchronously. You can use the await keyword only if the method is defined as async.

    The DataWriter.StoreAsync method throws an exception if data could not be sent over the TCP connection to network server. Use a try/catch block for any exceptions and print out the exception message in the StatusText UI element if an error occurs. In the try block, update the status.

            private async void Send_Click(object sender, RoutedEventArgs e)
            {
                if (!connected) {
                   StatusText.Text = "Must be connected to send!";
                   return;
                }
    
                Int32 len = 0; // Gets the UTF-8 string length.
    
                try
                {
                    OutputView.Text = "";
                    StatusText.Text = "Trying to send data ...";
    
                    // add a newline to the text to send
                    string sendData = SendText.Text + Environment.NewLine; 
                    DataWriter writer = new DataWriter(clientSocket.OutputStream);
                    Int32 len = writer.MeasureString(sendData); // Gets the UTF-8 string length.
    
                    // Call StoreAsync method to store the data to a backing stream
                    await writer.StoreAsync();
    
                    StatusText.Text = "Data was sent"+Environment.NewLine;
    
                    // detach the stream and close it
                    writer.DetachStream();
                    writer.Dispose();
    
                 }
                catch (Exception exception) {
                    // If this is an unknown status, 
                    // it means that the error is fatal and retry will likely fail.
                    if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
                        throw;
                     }
    
                    StatusText.Text = "Send data or receive failed with error: " + exception.Message;
                    // Could retry the connection, but for this simple example
                    // just close the socket.
    
                    closing = true;
                    clientSocket.Dispose();
                    clientSocket = null;
                    connected = false;
    
                }
    
                // Now try to receive data from server
                try
                {
                    OutputView.Text = "";
                    StatusText.Text = "Trying to receive data ...";
    
                    DataReader reader = new DataReader(clientSocket.InputStream);
                    // Set inputstream options so that we don't have to know the data size
                    reader.InputStreamOptions = Partial;
                    await reader.LoadAsync(reader.UnconsumedBufferLength);  
    
                 }
                catch (Exception exception) {
                    // If this is an unknown status, 
                    // it means that the error is fatal and retry will likely fail.
                    if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
                        throw;
                     }
    
                    StatusText.Text = "Receive failed with error: " + exception.Message;
                    // Could retry, but for this simple example
                    // just close the socket.
    
                    closing = true;
                    clientSocket.Dispose();
                    clientSocket = null;
                    connected = false;
    
                }
            }
    

Run the application

  • To run the app, press F5 in Microsoft Visual Studio Express 2012 for Windows 8 to run the project. Select the buttons in order to establish a connection, send data, and to close the socket.

Summary and next steps

In this topic, you created an app that uses a TCP stream socket to make a network connection and send data using a StreamSocket object.

You can also use a datagram socket to make a network connection to send data. For an example, see How to connect with a datagram socket.

Other resources

Connecting with sockets

How to configure network capabilities

How to connect with a datagram socket

How to secure socket connections with TLS/SSL

How to set timeouts on socket operations

How to use advanced socket controls

Troubleshoot and debug network connections

Reference

Windows.Networking

Windows.Networking.Connectivity

Windows.Networking.Sockets

Samples

StreamSocket sample