ストリーム ソケットを使って接続する方法 (HTML)

[ この記事は、Windows ランタイム アプリを作成する Windows 8.x および Windows Phone 8.x 開発者を対象としています。Windows 10 向けの開発を行っている場合は、「最新のドキュメント」をご覧ください]

このトピックでは、StreamSocket を使った Windows ランタイム アプリで TCP ソケットを使ってネットワーク データを送受信できるようにする方法を説明します。 TCP ソケットは、有効期間が長い接続用にどちらの方向にも下位レベルのネットワーク データ転送機能を提供します。 TCP ソケットは、インターネットで使われるほとんどのネットワーク プロトコルのベースとなる機能です。

サンプルのクライアント コンポーネントでは、TCP ソケットを作ってネットワーク接続を確立し、そのソケットを使ってデータを送信して、ソケットを閉じます。サンプルのサーバー コンポーネントでは、TCP ソケットを作ってネットワーク接続をリッスンおよび許可し、受信ソケットからの接続を許可し、そのソケットを使ってクライアントからデータを受信した後、ソケットを閉じます。このサンプルは、JavaScript、C#、VB、C++ プログラミング言語で用意されています。

サンプルのクライアント コンポーネントは、次の機能を示しています。

  • StreamSocket クラスを使って TCP ソケットを作る。
  • いずれかの StreamSocket.ConnectAsync メソッドを使って、TCP ネットワーク サーバーへのネットワーク接続を確立する。
  • プログラマが任意のストリームで共通する型 (整数や文字列など) を書き込むことができる Streams.DataWriter オブジェクトを使って、データをサーバーに送る。
  • ソケットを閉じる。

サンプルのサーバー コンポーネントは、次の機能を示しています。

  • StreamSocketListener クラスを使い、TCP ソケットを作って着信 TCP 接続をリッスンします。
  • StreamSocketListener.BindServiceNameAsync メソッドを使い、ソケットをローカル サービス名にバインドして着信ネットワーク接続をリッスンする。
  • 接続が StreamSocketListener オブジェクトで受信されたことを示す StreamSocketListener.ConnectionReceived イベントを受信する。
  • プログラマが任意のストリームで共通する型 (整数や文字列など) を読み取ることができる Streams.DataReader オブジェクトを使って、クライアントからデータを受け取る。
  • ソケットを閉じる。

  このサンプルを使うには、ループバック インターフェイスを使ったネットワーク アクセスが必要です。

 

必要条件

以下の例では JavaScript を使っています。初めてのアプリ作成の説明については、「JavaScript を使った初めての Windows ストア アプリの作成」をご覧ください。

Windows ストア アプリをネットワークに対応させるには、プロジェクトの Package.appxmanifest ファイルで該当する機能を設定する必要があります。 それぞれのネットワーク機能の定義について詳しくは、「ネットワーク分離機能を構成する方法」をご覧ください。

手順

新しいプロジェクトを作る

  1. Microsoft Visual Studio 2013 を開き、[ファイル] メニューの [新しいプロジェクト] をクリックします。
  2. テンプレートの一覧で、[JavaScript] を選びます。
  3. そのセクションで、[Store apps] を選びます。
  4. そのセクションで、[Universal Apps]、[Windows apps]、[Windows Phone apps] のいずれか (対象のプラットフォームに応じて異なります) を選び、[新しいアプリケーション] を選びます。
  5. アプリに socketsSample という名前を付け、[OK] をクリックします。

ネットワーク アクセスを有効にする機能を設定する

アプリにネットワーク アクセスが必要な場合、アプリのネットワーク機能を設定する必要があります。StreamSocket を使ってネットワーク サービスに接続するアプリでは、ネットワーク機能を設定する必要があります。

アプリがクライアントとしてインターネット上のリモート サービスに接続できるようにするには、インターネット (クライアント) 機能が必要です。アプリがクライアントとしてホーム ネットワークまたは社内ネットワーク上のリモート サービスに接続するには、プライベート ネットワーク (クライアントとサーバー) 機能が必要です。

アプリが StreamSocketListener を使ってインターネット上のリモート エンドポイントからの着信接続をリッスンする必要がある場合、インターネット (クライアントとサーバー) 機能が必要です。アプリが StreamSocketListener を使ってホーム ネットワークまたは社内ネットワーク上のリモート エンドポイントからの着信接続をリッスンする必要がある場合、プライベート ネットワーク (クライアントとサーバー) 機能が必要です。

  Windows Phone では、アプリに対してすべてのネットワーク アクセスを有効にするネットワーク機能は [インターネット (クライアントとサーバー)] だけです。

 

着信接続をリッスンしているこのサンプルのサーバー コンポーネントがクライアント コンポーネントと同じデバイスで実行されている場合、ループバック アクセスが必要です。Visual Studio 2013 で開発および実行されたアプリは、ループバックの制限から除外済みとして自動的に登録されます。詳しくは、「ループバックを有効にする方法とネットワーク分離のトラブルシューティングを行う方法」をご覧ください。

ネットワーク アクセスについて詳しくは、「ネットワーク分離機能を構成する方法」をご覧ください。

アプリからインターネット、ホーム ネットワークまたは社内ネットワーク上のネットワーク サービスにアクセスする場合は、アプリを展開する前に、これらの手順に従ってネットワーク機能を設定する必要があります。

  1. Microsoft Visual Studio を使って、package.appxmanifest ファイルを開きます。

  2. [機能] タブをクリックします。

  3. Windows バージョンのサンプルをビルドするには、[インターネット (クライアント)] 機能か [プライベート ネットワーク (クライアントとサーバー)] 機能を選びます。

    Windows Phone バージョンのサンプルをビルドするには、[インターネット (クライアントとサーバー)] 機能を選びます。

  4. マニフェスト ファイルを保存して閉じます。

HTML UI を追加する

  1. html フォルダーを開きます。新しい startListener.html ファイルを開き、次の HTML を <head> と <body> のセクションに追加します。

    <!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. html フォルダーを開きます。新しい connectToListener.html ファイルを開き、次の HTML を <head> と <body> のセクションに追加します。

    <!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. html フォルダーを開きます。新しい sendData.html ファイルを開き、次の HTML を <head> と <body> のセクションに追加します。

    <!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. html フォルダーを開きます。新しい closeSocket.html ファイルを開き、次の HTML を <head> と <body> のセクションに追加します。

    <!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>
    

サンプルとシナリオを定義する

この手順のコードでは、サンプル、HTML ファイル、サンプルで使用されるシナリオを定義しています。イベント リスナーの追加と、アプリの開始も行います。シナリオのオプションにより、ユーザーは、ソケット リスナーの開始、クライアントを開始してリスナーに接続すること、クライアントからサーバーへのデータ送信、ソケットを閉じることができます。

  • js フォルダーを開きます。default.js ファイルを開き、次のコードをファイルに追加します。

        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();
    

ソケットとイベント関数の変数を定義する

この手順のコードでは、リスナー ソケット、クライアント ソケット、サーバー読み取りソケットを始めとする多数の変数と、エラーとイベントに関するさまざまな変数を作成します。クライアント ソケットが接続状態と終了状態のいずれであるかを監視する変数が作成されます。この手順では、サーバー上で接続するホスト名とサービス名 (TCP ポート) も定義します。 このホスト名とサービス名の値は既定値として設定されますが、UI で変更できます。

  • js フォルダーを開きます。新しい socketsSample.js ファイルを開き、次のコードをファイルに追加します。

    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;
            }
        };
    })();
    

リスナーを作成し、サービス名 (ポート) のリッスンを開始します。

このセクションのコードでは、リスナーを作成して、リッスンを開始します。リスナーを IP アドレスと TCP ポートにバインドするようにユーザーが要求したときにイベントを処理し、接続を受け入れ、クライアントから送信されたデータを読み取る関数も追加されています。

  この特定の例は (クライアントとサーバーが同じアプリ内に存在する) 自己完結型ですが、通常はクライアントとサーバーのアプリは分離されています。

 

  • js フォルダーを開きます。新しい startListener.js ファイルを開き、次のコードをファイルに追加します。

        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);
            }
        }
    

ソケットを作成してリモート エンドポイントに接続する

この手順のコードでは、ソケットを作成し、StreamSocket.ConnectAsync メソッドを使ってリモート エンドポイント (通常はサーバー) に接続するための関数を追加します。 クライアントが接続を確立しようとしてエラーが発生した場合にエラーを処理する関数も追加されます。

  • js フォルダーを開きます。 新しい connectToListener.js ファイルを開き、次のコードをファイルに追加します。

        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);
            }
        }
    

クライアント上でデータを送受信する

この手順のコードでは、Windows.Storage.Stream.DataWriter クラスのメソッドを使ってサーバーにデータを送信するための関数を追加します。

  • js フォルダーを開きます。新しい sendData.js ファイルを開き、次のコードをファイルに追加します。

        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);
            }
        }
    

ソケットを閉じる

この手順のコードでは、StreamSocket.Close メソッドを使ってソケットを閉じます。 ソケットが閉じられると、保留中のすべての操作が終了し、エラー ルーチンが呼び出されます。

  • js フォルダーを開きます。 新しい socketClose.js ファイルを開き、次のコードをファイルに追加します。

        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.");
        }
    

アプリケーションを実行する

  • アプリを実行するには、Visual Studio で F5 キーを押して、プロジェクトを実行します。 ボタンをクリックすると、リスナーの開始、クライアントのリスナーへの接続、データの送信、ソケットを閉じる処理を行うことができます。

要約と次のステップ

このトピックでは、TCP ストリーム ソケットを使ってネットワーク接続を確立し、StreamSocket オブジェクトを使ってデータを送るアプリを作りました。このアプリで、TCP 接続をリッスンして、データの送受信に使用できるストリーム ソケットからの接続を受け入れる方法も説明しました。

このトピックのソース コードとビルド ファイルは、StreamSocket のサンプルとして用意されています。

また、データグラム ソケットを使ってネットワーク接続を確立してデータを送ることもできます。例として、「データグラム ソケットを使って接続する方法」をご覧ください。

関連トピック

その他のリソース

ソケットを使った接続

ネットワーク分離機能を構成する方法

データグラム ソケットを使って接続する方法

ループバックを有効にする方法とネットワーク分離のトラブルシューティングを行う方法

TLS/SSL を使ってソケット接続のセキュリティを確保する方法

ソケット操作にタイムアウトを設定する方法

ソケットの詳細制御の使い方

ネットワーク接続のトラブルシューティングとデバッグ

辞書/リファレンス

StreamSocket

StreamSocketListener

Windows.Networking

Windows.Networking.Sockets

Windows.Storage.Stream.DataWriter

サンプル

StreamSocket のサンプル