共用方式為


如何使用資料流通訊端進行連線 (HTML)

[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]

本主題說明 如何讓 Windows 執行階段應用程式利用使用 StreamSocket 的 TCP 通訊端來傳送與接收網路資料。 TCP 通訊端為長期連線提供雙向的低階網路資料傳輸。 TCP 通訊端是網際網路上大多數網路通訊協定所使用的基本功能。

範例的用戶端元件會建立一個 TCP 通訊端來建立網路連線,使用通訊端傳送和接收資料,然後關閉通訊端。範例的伺服器元件會建立一個 TCP 通訊端接聽和接受網路連線、接受來自連入通訊端的連線、使用通訊端接收用戶端的資料,然後關閉通訊端。此範例以 JavaScript、C#、VB 和 C++ 程式設計語言提供。

範例的用戶端元件示範以下功能:

  • 使用 StreamSocket 類別建立 TCP 通訊端。
  • 使用其中一個 StreamSocket.ConnectAsync 方法建立與 TCP 網路伺服器的網路連線。
  • 使用 Streams.DataWriter 物件將資料傳送到伺服器,這樣可讓程式設計人員在任何資料流上撰寫常用類型 (例如,整數和字串)。
  • 關閉通訊端。

範例的伺服器元件示範以下功能:

注意  使用此範例需要利用回送介面的網路存取。

 

先決條件

下列範例使用 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,然後按一下 [確定]****。

設定功能以啟用網路存取

如果應用程式需要存取網路,您需要為應用程式設定網路功能。使用 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 範例