データグラム ソケットを使って接続する方法 (HTML)
[ この記事は、Windows ランタイム アプリを作成する Windows 8.x および Windows Phone 8.x 開発者を対象としています。Windows 10 向けの開発を行っている場合は、「最新のドキュメント」をご覧ください]
このトピックでは、DatagramSocket を使った Windows ランタイム アプリで UDP を使ってネットワーク データを送受信する方法を説明します。
サンプルのクライアント コンポーネントでは、UDP ソケットを作り、そのソケットを使ってデータを送受信した後、ソケットを閉じます。サンプルのサーバー コンポーネントでは、着信ネットワーク パケットをリッスンする UDP ソケットを作り、クライアントから着信 UDP パケットを受信してクライアントにデータを送信した後、ソケットを閉じます。このサンプルは、JavaScript、C#、C++ プログラミング言語で用意されています。
サンプルのクライアント コンポーネントは、次の機能を示しています。
- DatagramSocket クラスを使って、クライアントの UDP ソケットを作ってデータを送受信する。
- UDP データグラムが DatagramSocket オブジェクトで受信されたことを示す DatagramSocket.MessageReceived イベントのハンドラーを追加する。
- いずれかの DatagramSocket.ConnectAsync メソッドをつかってパケットを送信する必要がある UDP ネットワーク サーバーのリモート エンドポイントを設定する。
- プログラマが任意のストリームで共通する型 (整数や文字列など) を書き込むことができる Streams.DataWriter オブジェクトを使って、データをサーバーに送る。
- ソケットを閉じる。
サンプルのサーバー コンポーネントは、次の機能を示しています。
- DatagramSocket クラスを使い、UDP ソケットを作って着信データグラム パケットと送信パケットをリッスンし、受信する。
- UDP データグラムが DatagramSocket オブジェクトで受信されたことを示す DatagramSocket.MessageReceived イベントのハンドラーを追加する。
- DatagramSocket.BindServiceNameAsync メソッドを使い、ソケットをローカル サービス名にバインドして着信 UDP パケットをリッスンする。
- UDP データグラムが DatagramSocket オブジェクトで受信されたことを示す DatagramSocket.MessageReceived イベントを受信する。
- DatagramSocket.MessageReceived ハンドラーを使ってクライアントからデータを受信する。DatagramSocket.MessageReceived ハンドラーに渡される DatagramSocketMessageReceivedEventArgs オブジェクトにより、アプリがクライアントからデータを受信し、データを送信したリモート アドレスとポートを決定することもできます。
- ソケットを閉じる。
注 このサンプルを使うには、ループバック インターフェイスを使ったネットワーク アクセスが必要です。
目標: DatagramSocket ソケットを使って他のコンピューターまたはデバイスへのネットワーク接続を作る
必要条件
以下の例では JavaScript を使っています。初めてのアプリ作成の説明については、「JavaScript を使った初めての Windows ストア アプリの作成」をご覧ください。
Windows ストア アプリをネットワークに対応させるには、プロジェクトの Package.appxmanifest ファイルで該当する機能を設定する必要があります。 それぞれのネットワーク機能の定義について詳しくは、「ネットワーク分離機能を構成する方法」をご覧ください。
手順
1. 新しいプロジェクトを作る
- Microsoft Visual Studio 2013 を開き、[ファイル] メニューの [新しいプロジェクト] をクリックします。
- テンプレートの一覧で、[JavaScript] を選びます。
- そのセクションで、[Store apps] を選びます。
- そのセクションで、[Universal Apps]、[Windows apps]、[Windows Phone apps] のいずれか (対象のプラットフォームに応じて異なります) を選び、[新しいアプリケーション] を選びます。
- アプリに
socketsSample
という名前を付け、[OK] をクリックします。
2. ネットワーク アクセスを有効にする機能を設定する
アプリにネットワーク アクセスが必要な場合、アプリのネットワーク機能を設定する必要があります。DatagramSocket を使ってネットワーク サービスに接続するアプリでは、ネットワーク機能を設定する必要があります。
アプリがクライアントとしてインターネット上のリモート サービスに接続できるようにするには、インターネット (クライアント) 機能が必要です。アプリがクライアントとしてホーム ネットワークまたは社内ネットワーク上のリモート サービスに接続するには、プライベート ネットワーク (クライアントとサーバー) 機能が必要です。
アプリが DatagramSocket を使ってインターネット上のリモート エンドポイントからの着信接続をリッスンする必要がある場合、インターネット (クライアントとサーバー) 機能が必要です。アプリが DatagramSocket を使ってホーム ネットワークまたは社内ネットワーク上のリモート エンドポイントからの着信接続をリッスンする必要がある場合、プライベート ネットワーク (クライアントとサーバー) 機能が必要です。
注 Windows Phone では、アプリに対してすべてのネットワーク アクセスを有効にするネットワーク機能は [インターネット (クライアントとサーバー)] だけです。
着信接続をリッスンしているこのサンプルのサーバー コンポーネントがクライアント コンポーネントと同じデバイスで実行されている場合、ループバック アクセスが必要です。Visual Studio 2013 で開発および実行されたアプリは、ループバックの制限から除外済みとして自動的に登録されます。詳しくは、「ループバックを有効にする方法とネットワーク分離のトラブルシューティングを行う方法」をご覧ください。
ネットワーク アクセスについて詳しくは、「ネットワーク分離機能を構成する方法」をご覧ください。
アプリからインターネット、ホーム ネットワークまたは社内ネットワーク上のネットワーク サービスにアクセスする場合は、アプリを展開する前に、これらの手順に従ってネットワーク機能を設定する必要があります。
Microsoft Visual Studio を使って、package.appxmanifest ファイルを開きます。
[機能] タブをクリックします。
Windows バージョンのサンプルをビルドするには、[インターネット (クライアント)] 機能か [プライベート ネットワーク (クライアントとサーバー)] 機能を選びます。
Windows Phone バージョンのサンプルをビルドするには、[インターネット (クライアントとサーバー)] 機能を選びます。
マニフェスト ファイルを保存して閉じます。
3. HTML UI を追加する
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> DatagramSocket is used to create the 'server' side of a connection. It listens on a 'service name' (often a port number) and each time a datagram is received on the port number it fires MessageReceived event. </p> <p> <label for="serviceNameAccept">Service Name:</label> <input id="serviceNameAccept" type="text" /> </p> <p> <button id="buttonStartListener">Listen</button> </p> </div> <div data-win-control="SdkSample.ScenarioOutput"> <p id="statusBox"></p> <p id="outputBox"></p> </div> </body> </html>
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 internationalized 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" disabled="disabled" /> </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>
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. </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>
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>
4. サンプルとシナリオを定義する
この手順のコードでは、サンプル、HTML ファイル、サンプルで使用されるシナリオを定義しています。イベント リスナーの追加と、アプリの開始も行います。シナリオのオプションにより、ユーザーは、ソケット リスナーの開始、クライアントを開始してリスナーに接続すること、クライアントからサーバーへのデータ送信、ソケットを閉じることができます。
js フォルダーを開きます。default.js ファイルを開き、次のコードをファイルに追加します。
var sampleTitle = "DatagramSocket"; var scenarios = [ { url: "/html/startListener.html", title: "Start DatagramSocket Listener" }, { 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();
5. ソケットとイベント関数の変数を定義する
この手順のコードでは、リスナー ソケット、クライアント ソケット、エラーとイベントに関するさまざまな変数を作ります。クライアント ソケットが接続状態と終了状態のいずれであるかを監視する変数が作成されます。この手順では、接続先およびデータの送信先のホスト名とサービス名 (UDP ポート)、およびデータを受け入れ、受信するローカル サービス名 (UDP ポート) も定義します。 リモート ホスト名、リモート サービス名、ローカル サービス名の値は、既定値として設定されますが、UI で変更できます。
js フォルダーを開きます。新しい socketsSample.js ファイルを開き、次のコードをファイルに追加します。
var socketsSample = {}; (function () { "use strict"; socketsSample.listener = null; socketsSample.listenerOutputStream = null; socketsSample.listenerPeerAddress = null; socketsSample.listenerPeerPort = null; socketsSample.clientSocket = null; socketsSample.clientDataWriter = null; socketsSample.connected = false; socketsSample.closing = false; socketsSample.bindingToService = false; socketsSample.serviceNameAccept = "22112"; socketsSample.hostNameConnect = "localhost"; socketsSample.serviceNameConnect = "22112"; socketsSample.close = function () { socketsSample.closing = true; if (socketsSample.listener) { socketsSample.listener.close(); } if (socketsSample.clientSocket) { socketsSample.clientSocket.close(); } socketsSample.listener = null; socketsSample.listenerOutputStream = null; socketsSample.listenerPeerAddress = null; socketsSample.listenerPeerPort = null; socketsSample.clientSocket = null; socketsSample.clientDataWriter = null; socketsSample.connected = false; }; 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; } }; })();
6. リスナーを作成し、サービス名 (ポート) のリッスンを開始します。
このセクションのコードでは、リスナーを作成して、リッスンを開始します。リスナーを IP アドレスと UDP ポートにバインドするようにユーザーが要求したときにイベントを処理し、接続を受け入れ、クライアントから送られたデータを読み取る関数も追加されています。
注 この特定の例は (クライアントとサーバーが同じアプリ内に存在する) 自己完結型ですが、通常はクライアントとサーバーのアプリは分離されています。
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() { var serviceName = document.getElementById("serviceNameAccept").value; if (serviceName === "") { socketsSample.displayStatus("Please provide a service name."); return; } if (socketsSample.listener) { socketsSample.displayStatus("Already have a listener; call close to close the listener."); return; } socketsSample.closing = false; socketsSample.bindingToService = true; socketsSample.listener = new Windows.Networking.Sockets.DatagramSocket(); socketsSample.listener.addEventListener("messagereceived", onServerMessageReceived); socketsSample.displayStatus("Server: listener creation started."); socketsSample.listener.bindServiceNameAsync(serviceName).done(function () { socketsSample.displayStatus("Server: listener creation completed."); socketsSample.bindingToService = false; }, onError); } function onServerMessageReceived(eventArgument) { if (socketsSample.listenerOutputStream) { echoMessage(socketsSample.listenerOutputStream, eventArgument); return; } socketsSample.listener.getOutputStreamAsync(eventArgument.remoteAddress, eventArgument.remotePort).done(function (outputStream) { if (!socketsSample.listenerOutputStream) { socketsSample.listenerOutputStream = outputStream; socketsSample.listenerPeerAddress = eventArgument.remoteAddress; socketsSample.listenerPeerPort = eventArgument.remotePort; } echoMessage(socketsSample.listenerOutputStream, eventArgument); }); } function echoMessage(outputStream, eventArgument) { if (socketsSample.listenerPeerAddress !== eventArgument.remoteAddress || socketsSample.listenerPeerPort !== eventArgument.remotePort) { socketsSample.displayStatus("Got datagram from " + eventArguments.remoteAddress + ":" + eventArguments.remotePort + ", but already 'connected' to " + socketsSample.listenerPeerAddress + ":" + socketsSample.listenerPeerPort); return; } outputStream.writeAsync(eventArgument.getDataReader().detachBuffer()).done(function () { // Do nothing - client will print out a message when data is received. }); } function onError(reason) { // Clean up a listener if we failed to bind to a port. if (socketsSample.bindingToService) { socketsSample.listener = null; socketsSample.bindingToService = false; } // 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); } }
7. ソケットを作成してリモート エンドポイントに接続する
この手順のコードでは、ソケットを作り、DatagramSocket.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() { var serviceName = document.getElementById("serviceNameConnect").value; if (serviceName === "") { socketsSample.displayStatus("Please provide a service name."); return; } // By default 'hostNameConnect' is disabled and host name validation is not required. When enabling the text // box validating the host name is required since it was received from an untrusted source (user input). // Note that when enabling the text box users may provide names for hosts on the intErnet that require the // "Internet (Client)" capability. var hostName; try { hostName = new Windows.Networking.HostName(document.getElementById("hostNameConnect").value); } catch (error) { socketsSample.displayStatus("Error: Invalid host name."); return; } if (socketsSample.clientSocket) { socketsSample.displayStatus("Already have a client; call close to close the listener and the client."); return; } socketsSample.closing = false; socketsSample.clientSocket = new Windows.Networking.Sockets.DatagramSocket(); socketsSample.clientSocket.addEventListener("messagereceived", onMessageReceived); socketsSample.displayStatus("Client: connection started."); socketsSample.clientSocket.connectAsync(hostName, serviceName).done(function () { socketsSample.displayStatus("Client: connection completed."); socketsSample.connected = true; }, onError); } function onMessageReceived(eventArgument) { try { var messageLength = eventArgument.getDataReader().unconsumedBufferLength; var message = eventArgument.getDataReader().readString(messageLength); socketsSample.displayStatus("Client: receive message from server \"" + message + "\""); } catch (exception) { status = Windows.Networking.Sockets.SocketError.getStatus(exception.number); if (status === Windows.Networking.Sockets.SocketErrorStatus.connectionResetByPeer) { socketsSample.displayStatus("Peer does not listen on the specific port. Please make sure that you run step 1 first " + "or you have a server properly working on a remote server."); } else { socketsSample.displayStatus("Error happened when receiving a datagram: " + exception.message); } } } 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); } }
8. クライアント上でデータを送受信する
この手順のコードでは、Windows.Storage.Streams.DataWriter クラスのメソッドを使ってリモート UDP エンドポイントにデータを送るための関数を追加します。
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; } if (!socketsSample.clientDataWriter) { socketsSample.clientDataWriter = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream); } var string = "Hello World"; socketsSample.clientDataWriter.writeString(string); socketsSample.displayStatus("Client sending: " + string + "."); socketsSample.clientDataWriter.storeAsync().done(function () { socketsSample.displayStatus("Client sent: " + string + "."); }, 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); } }
9. ソケットを閉じる
この手順のコードでは、DatagramSocket.Close メソッドを使ってソケットを閉じます。 ソケットが閉じられると、保留中のすべての操作が終了し、エラー ルーチンが呼び出されます。
js フォルダーを開きます。 新しい socketClose.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; } if (!socketsSample.clientDataWriter) { socketsSample.clientDataWriter = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream); } var string = "Hello World"; socketsSample.clientDataWriter.writeString(string); socketsSample.displayStatus("Client sending: " + string + "."); socketsSample.clientDataWriter.storeAsync().done(function () { socketsSample.displayStatus("Client sent: " + string + "."); }, 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); } }
10. アプリケーションを実行する
- アプリを実行するには、Visual Studio で F5 キーを押して、プロジェクトを実行します。 ボタンをクリックすると、リスナーの開始、クライアントのリスナーへの接続、データの送信、ソケットを閉じる処理を行うことができます。
要約と次のステップ
このトピックでは、UDP データグラム ソケットを使ってネットワーク接続を確立し、DatagramSocket オブジェクトを使ってデータを送るアプリを作りました。アプリでは、UDP ポートをリッスンし、データを受け取る方法も示しました。
このトピックのソース コードとビルド ファイルは、DatagramSocket のサンプルとして用意されています。
また、ストリーム ソケットを使ってネットワーク接続を作り、データを送受信することもできます。例として、「ストリーム ソケットを使って接続する方法」をご覧ください。
関連トピック
その他のリソース
辞書/リファレンス
サンプル