How to connect with a stream socket (HTML)

This topic shows how to enable a Windows Runtime app to send and receive network data with a TCP 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 client component of the sample creates a TCP socket to make a network connection, uses the socket to send data, and closes the socket. The server component of the sample creates a TCP socket to listen for and accept network connections, accepts connections from the incoming socket, uses the socket to receive data from the client, and closes the socket. This sample is provided in the JavaScript, C#, VB, and C++ programming languages.

The client component of 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 any stream.
  • Close the socket.

The server component of the sample demonstrates the following features:

Note  Use of this sample requires network access using the loopback interface.

 

Prerequisites

The following examples use JavaScript. For help creating your first app, see Create your first Windows Store app using JavaScript.

To ensure your Windows Store app is network ready, you must set the capability in the project Package.appxmanifest file. For a definition of each network capability, see How to configure network isolation capabilities.

Instructions

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 JavaScript.
  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 socketsSample and click OK.

Set capabilities to enable network access

You need to set network capabilities for your app if the app needs network access. An app that uses a StreamSocket to connect to a network service 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.

If the app needs to use the StreamSocketListener to listen for incoming connections from remote endpoints on the Internet, then the Internet (Client & Server) capability is needed. If the app needs to use the StreamSocketListener to listen for incoming connections from remote endpoints 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.

 

If the server component of this sample listening for incoming connections is running on the same device as the client component, this would require loopback access. Apps developed and run in Visual Studio 2013 will automatically be registered as being exempt from the loopback restrictions. For more information, see How to enable loopback and debug network isolation.

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

These steps are needed to set network capabilities for an app before it is deployed if it accesses a network service on the Internet or on a home or work network.

  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 HTML UI

  1. Open the html folder. Open a new startListener.html file and add the following HTML to the <head> and <body> sections.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/socketsSample.js"></script>
        <script src="/js/startListener.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                StreamSocketListener will create the "server" side of a connection. It listens on
                a "service name" (often a port number) and calls a callback when it accepts a connection;
                this happens when some other application tries to connect. Once a connection is
                accepted, the acceptAsync() method needs to be called again.
            </p>
            <p>
                <label for="serviceNameAccept">Service Name:</label>
                <input id="serviceNameAccept" type="text" />
            </p>
            <p>
                <button id="buttonStartListener">Create StreamSocketListener and start to listen</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  2. Open the html folder. Open a new connectToListener.html file and add the following HTML to the <head> and <body> sections.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/connectToListener.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Next, you need the "other side of the connection" -- you need to connect to a listener.
                The host name and service name (often a port number) to connect to are the "Host
                Name:" and "Service Name:" entries. The service name should match what you started
                to listen to!
            </p>
            <p>
                The connection will automatically use IPv6 as needed. It will also resolve international
                domain names.
            </p>
            <p>
                Due to the network security system, you cannot connect to other applications running
                on the same machine. This means that you can only use "localhost" to connect to
                the same application (specifically, you can connect to a listener on the same machine
                running in the same app container)
            </p>
            <p>
                <label for="hostNameConnect">Host Name:</label>
                <input id="hostNameConnect" type="text" />
            </p>
            <p>
                <label for="serviceNameConnect">Service Name:</label>
                <input id="serviceNameConnect" type="text" />
            </p>
            <p>
                <button id="buttonOpen">Connect Now</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  3. Open the html folder. Open a new sendData.html file and add the following HTML to the <head> and> <body> sections.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/sendData.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Now you can send data to the "server". Sending data is often done with the DataWriter
                object; it will write to the socket stream. You can also hook up the socket stream
                to other streams in Windows 8.
            </p>
            <p>
                <button id="buttonSend">Send 'hello' now</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  4. Open the html folder. Open a new closeSocket.html file and add the following HTML to the <head> and <body> sections.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/closeSocket.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>Lastly, you can close all sockets.</p>
            <p>If you don't close your socket, it will be closed for you when the application exits.</p>
            <p>
                <button id="buttonClose">Close all sockets</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    

Define the sample and the scenarios

The code in this step defines the sample, the HTML files, and the scenarios that are used by the sample. The code also add event listeners and starts the app. The scenario options allow the user to start the socket listener, start the client to connect to the listener, have the client send some data to the server, and close the sockets.

  • Open the js folder. Open the default.js file and add the following code to the file.

        var sampleTitle = "StreamSocket";
    
        var scenarios = [
            { url: "/html/startListener.html", title: "Start StreamSocketListener" },
            { url: "/html/connectToListener.html", title: "Connect to Listener" },
            { url: "/html/sendData.html", title: "Send Data" },
            { url: "/html/closeSocket.html", title: "Close Socket" }
        ];
    
        function activated(eventObject) {
            if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
                // Use setPromise to indicate to the system that the splash screen must not be torn down
                // until after processAll and navigate complete asynchronously.
                eventObject.setPromise(WinJS.UI.processAll().then(function () {
                    // Navigate to either the first scenario or to the last running scenario
                    // before suspension or termination.
                    var url = WinJS.Application.sessionState.lastUrl || scenarios[0].url;
                    return WinJS.Navigation.navigate(url);
                }));
            }
        }
    
        WinJS.Navigation.addEventListener("navigated", function (eventObject) {
            var url = eventObject.detail.location;
            var host = document.getElementById("contentHost");
            // Call unload method on current scenario, if there is one
            host.winControl && host.winControl.unload && host.winControl.unload();
            WinJS.Utilities.empty(host);
            eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {
                WinJS.Application.sessionState.lastUrl = url;
            }));
        });
    
        WinJS.Namespace.define("SdkSample", {
            sampleTitle: sampleTitle,
            scenarios: scenarios
        });
    
        WinJS.Application.addEventListener("activated", activated, false);
        WinJS.Application.start();
    

Define variables for sockets and event functions

The code in this step creates a number of variables including the listener socket, the client socket, the server read socket, and various variable for errors and event. 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.

  • Open the js folder. Open a new socketsSample.js file and add the following code to the file.

    var socketsSample = {};
    
    (function () {
        "use strict";
    
        socketsSample.listener = null; // A StreamSocketListener that acts as our server.
        socketsSample.serverSocket = null; // The server socket that's been accepted.
        socketsSample.serverReader = null; // The reader for the server socket.
        socketsSample.clientSocket = null; // The client socket that will connect to the server socket.
        socketsSample.connected = false;
        socketsSample.closing = false;
    
        socketsSample.serviceNameAccept = "22112";
        socketsSample.hostNameConnect = "localhost";
        socketsSample.serviceNameConnect = "22112";
    
        socketsSample.displayStatus = function (message) {
            document.getElementById("statusBox").innerHTML = message;
        };
    
        socketsSample.displayOutput = function (message) {
            document.getElementById("outputBox").innerHTML = message;
        };
    
        socketsSample.setValues = function () {
            var serviceNameAcceptInput = document.getElementById("serviceNameAccept");
            var hostNameConnectInput = document.getElementById("hostNameConnect");
            var serviceNameConnectInput = document.getElementById("serviceNameConnect");
    
            if (serviceNameAcceptInput) {
                serviceNameAcceptInput.value = socketsSample.serviceNameAccept;
            }
            if (hostNameConnectInput) {
                hostNameConnectInput.value = socketsSample.hostNameConnect;
            }
            if (serviceNameConnectInput) {
                serviceNameConnectInput.value = socketsSample.serviceNameConnect;
            }
        };
    
        socketsSample.getValues = function (evt) {
            switch (evt.target.id) {
                case "serviceNameAccept":
                    socketsSample.serviceNameAccept = evt.target.value;
                    break;
                case "hostNameConnect":
                    socketsSample.hostNameConnect = evt.target.value;
                    break;
                case "serviceNameConnect":
                    socketsSample.serviceNameConnect = evt.target.value;
                    break;
            }
        };
    })();
    

Create a listener and start listening to a service name (port)

The code in this section creates a listener and starts listening. There are also functions added to handle events when the user requests that the listener bind to an IP address and TCP port, accept an connection, and read data sent from the client.

Note  Although this specific example is self-contained (client and server are in the same app), you would normally have separate client and server apps.

 

  • Open the js folder. Open a new startListener.js file and add the following code to the file:

        var page = WinJS.UI.Pages.define("/html/startListener.html", {
            ready: function (element, options) {
                document.getElementById("buttonStartListener").addEventListener("click", startListener, false);
                document.getElementById("serviceNameAccept").addEventListener("change", socketsSample.getValues, false);
                socketsSample.setValues();
            }
        });
    
        function startListener() {
            if (socketsSample.listener) {
                socketsSample.displayStatus("Already have a listener; call close to close the listener.");
                return;
            }
            socketsSample.closing = false;
            var serviceName = document.getElementById("serviceNameAccept").value;
            socketsSample.listener = new Windows.Networking.Sockets.StreamSocketListener(serviceName);
            socketsSample.listener.addEventListener("connectionreceived", onServerAccept);
            socketsSample.displayStatus("Server: listener creation started.");
            socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
                socketsSample.displayStatus("Server: listener creation completed.");
            }, onError);
        }
    
        // This has to be a real function ; it will "loop" back on itself with the
        // call to acceptAsync at the very end.
        function onServerAccept(eventArgument) {
            socketsSample.displayStatus("Server: connection accepted.");
            socketsSample.serverSocket = eventArgument.socket;
            socketsSample.serverReader = new Windows.Storage.Streams.DataReader(socketsSample.serverSocket.inputStream);
            startServerRead();
        }
    
        // The protocol here is simple: a four-byte 'network byte order' (big-endian) integer
        // that says how long a string is, and then a string that is that long.
        // We wait for exactly 4 bytes, read in the count value, and then wait for
        // count bytes, and then display them.
        function startServerRead() {
            socketsSample.serverReader.loadAsync(4).done(function (sizeBytesRead) {
                // Make sure 4 bytes were read.
                if (sizeBytesRead !== 4) {
                    socketsSample.displayStatus("Server: connection lost.");
                    return;
                }
    
                // Read in the 4 bytes count and then read in that many bytes.
                var count = socketsSample.serverReader.readInt32();
                return socketsSample.serverReader.loadAsync(count).then(function (stringBytesRead) {
                    // Make sure the whole string was read.
                    if (stringBytesRead !== count) {
                        socketsSample.displayStatus("Server: connection lost.");
                        return;
                    }
                    // Read in the string.
                    var string = socketsSample.serverReader.readString(count);
                    socketsSample.displayOutput("Server read: " + string);
                    // Restart the read for more bytes.
                    startServerRead();
                }); // End of "read in rest of string" function.
            }, onError);
        }
    
        function onError(reason) {
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

Create the socket and connect to a remote endpoint

The code in this step adds a function to create the socket and connect to the remote endpoint, typically a server, using the StreamSocket.ConnectAsync method. A function is also added to handle cases when there is an error when the client tries to make a connection.

  • Open the js folder. Open a new connectToListener.js file and add the following code to the file:

        var page = WinJS.UI.Pages.define("/html/connectToListener.html", {
            ready: function (element, options) {
                document.getElementById("buttonOpen").addEventListener("click", openClient, false);
                document.getElementById("hostNameConnect").addEventListener("change", socketsSample.getValues, false);
                document.getElementById("serviceNameConnect").addEventListener("change", socketsSample.getValues, false);
                socketsSample.setValues();
            }
        });
    
        function openClient() {
            if (socketsSample.clientSocket) {
                socketsSample.displayStatus("Already have a client; call close to close the listener and the client.");
                return;
            }
            socketsSample.closing = false;
            var serverHostName = new Windows.Networking.HostName(document.getElementById("hostNameConnect").value);
            var serviceName = document.getElementById("serviceNameConnect").value;
            socketsSample.clientSocket = new Windows.Networking.Sockets.StreamSocket();
            socketsSample.displayStatus("Client: connection started.");
            socketsSample.clientSocket.connectAsync(serverHostName, serviceName).done(function () {
                socketsSample.displayStatus("Client: connection completed.");
                socketsSample.connected = true;
            }, onError);
        }
    
        function onError(reason) {
            socketsSample.clientSocket = null;
    
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

Send and receive data on the client

The code in this step adds a function to send data to the server using methods on the Windows.Storage.Stream.DataWriter class.

  • Open the js folder. Open a new sendData.js file and add the following code to file:

        var page = WinJS.UI.Pages.define("/html/sendData.html", {
            ready: function (element, options) {
                document.getElementById("buttonSend").addEventListener("click", sendHello, false);
            }
        });
    
        function sendHello() {
            if (!socketsSample.connected) {
                socketsSample.displayStatus("Client: you must connect the client before using it.");
                return;
            }
            var writer = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
            var string = "Hello World";
            var len = writer.measureString(string); // Gets the UTF-8 string length.
            writer.writeInt32(len);
            writer.writeString(string);
            socketsSample.displayStatus("Client sending: " + string + ".");
            writer.storeAsync().done(function () {
                socketsSample.displayStatus("Client sent: " + string + ".");
                writer.detachStream();
            }, onError);
        }
    
        function onError(reason) {
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

Close the sockets

The code in this step will close the sockets using the StreamSocket.Close method. When sockets are closed, all pending operations will end and the error routines are called.

  • Open the js folder. Open a new socketClose.js file and add the following code to the file:

        var page = WinJS.UI.Pages.define("/html/closeSocket.html", {
            ready: function (element, options) {
                document.getElementById("buttonClose").addEventListener("click", closeListenerAndSockets, false);
            }
        });
    
        function closeListenerAndSockets() {
            socketsSample.closing = true;
            if (socketsSample.listener) {
                socketsSample.listener.close();
                socketsSample.listener = null;
            }
            if (socketsSample.serverSocket) {
                socketsSample.serverSocket.close();
                socketsSample.serverSocket = null;
            }
            if (socketsSample.clientSocket) {
                socketsSample.clientSocket.close();
                socketsSample.clientSocket = null;
                socketsSample.connected = false;
            }
            socketsSample.displayStatus("Client and server closed.");
        }
    

Run the application

  • To run the app, press F5 in Visual Studio to run the project. Select the buttons in order to start the listener, to connect the client to the listener, to send data, and to close sockets.

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. The app also showed how to listen for a TCP connection and accept a connection from a stream socket that can be used for sending or receiving data.

The source code and build files for this topic are available as the StreamSocket sample.

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 enable loopback and debug network isolation

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

StreamSocket

StreamSocketListener

Windows.Networking

Windows.Networking.Sockets

Windows.Storage.Stream.DataWriter

Samples

StreamSocket sample