如何使用資料包通訊端進行連線 (HTML)
[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]
本主題說明 如何在利用 DatagramSocket 的 Windows 執行階段應用程式使用 UDP 傳送和接收網路資料。
範例的用戶端元件會建立一個 UDP 通訊端,使用通訊端傳送和接收資料,然後關閉通訊端。範例的伺服器元件會建立一個 UDP 通訊端接聽連入網路封包、接收來自用戶端的連入 UDP 封包、傳送資料給用戶端,然後關閉通訊端。此範例以 JavaScript、C# 和 C++ 程式設計語言提供。
範例的用戶端元件示範以下功能:
- 使用 DatagramSocket 類別建立 UDP 通訊端,供用戶端傳送和接收資料。
- 新增 DatagramSocket.MessageReceived 事件的處理常式,指出 DatagramSocket 物件已收到 UDP 資料包。
- 設定 UDP 網路伺服器的遠端端點,在這裡應使用其中一個 DatagramSocket.ConnectAsync 方法傳送封包。
- 使用 Streams.DataWriter 物件將資料傳送到伺服器,這樣可讓程式設計人員在任何資料流上撰寫常用類型 (例如,整數和字串)。
- 關閉通訊端。
範例的伺服器元件示範以下功能:
- 使用 DatagramSocket 類別建立 UDP 通訊端,用於接聽和接收連入資料包封包和傳送封包。
- 新增 DatagramSocket.MessageReceived 事件的處理常式,指出 DatagramSocket 物件已收到 UDP 資料包。
- 將通訊端繫結到本機服務名稱,以使用 DatagramSocket.BindServiceNameAsync 方法接聽連入 UDP 封包。
- 接收指出 DatagramSocket 物件已收到 UDP 資料包的 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
,然後按一下 [確定]****。
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 範例取得。
您也可以使用資料流通訊端來建立網路連線,用以傳送和接收資料。如需相關範例,請參閱如何使用資料流通訊端進行連線。
相關主題
其他資源
參考
範例