Подключение с помощью сокета потока (HTML)
[ Эта статья адресована разработчикам приложений среды выполнения Windows для Windows 8.x и Windows Phone 8.x. При разработке приложений для Windows 10 см. раздел последняя документация]
В этом разделе: как использовать StreamSocket для отправки и получения приложением среды выполнения Windows сетевых данных через сокет TCP. Для постоянных подключений сокет TCP обеспечивает в сети низкоуровневую двунаправленную передачу данных. Сокеты TCP широко используются большинством сетевых протоколов в Интернете.
В клиентском компоненте этого примера создается TCP-сокет для установки сетевого подключения. Этот сокет используется для отправки и получения данных, а затем закрывается. В серверном компоненте этого примера создается TCP-сокет для ожидания и принятия сетевых подключений и принимаются подключения от входящего сокета. Сокет используется для получения данных от клиента, а затем закрывается. Этот пример предоставляется на JavaScript, C#, VB и C++.
Клиентский компонент примера демонстрирует следующие функции:
- Использование класса StreamSocket для создания TCP-сокета.
- Создание сетевого подключения к сетевому TCP-серверу с помощью одного из методов StreamSocket.ConnectAsync.
- Отправка данных на сервер с помощью объекта Streams.DataWriter, который позволяет программисту записывать общие типы данных (например, целочисленные и строковые) в любой поток.
- Закрытие сокета.
Серверный компонент примера демонстрирует следующие функции:
- Использование класса StreamSocketListener для создания TCP-сокета, который будет ожидать входящего подключения TCP.
- Привязка сокета к имени локальной службы для ожидания входящего сетевого подключения с помощью метода StreamSocketListener.BindServiceNameAsync.
- Получение события StreamSocketListener.ConnectionReceived, указывающего, что подключение было получено на объекте StreamSocketListener.
- Получение данных от клиента с помощью объекта Streams.DataReader, который позволяет программисту считывать общие типы данных (например, целочисленные и строковые) из любого потока.
- Закрытие сокета.
Примечание Использование данного примера требует доступа к сети с использованием интерфейса замыкания на себя.
Необходимые условия
В следующих примерах используется JavaScript. Указания по созданию первого приложения см. в разделе Создание первого приложения Магазина Windows на JavaScript.
Чтобы подготовить приложение Магазина Windows для работы в сети, необходимо настроить такую возможность в файле проекта Package.appxmanifest. Определение всех сетевых возможностей см. в разделе Настройка возможностей сетевой изоляции.
Инструкции
Создание нового проекта
- Откройте Microsoft Visual Studio 2013 и выберите Создать проект в меню Файл.
- В списке шаблонов выберите JavaScript.
- В разделе выберите Store apps.
- В этом разделе выберите Universal Apps, Windows apps или Windows Phone apps (в зависимости от целевой платформы), а затем — Пустое приложение.
- Назовите приложение
socketsSample
и нажмите ОК.
Задание характеристик для обеспечения доступа к сети
Если приложению нужен доступ к сети, для него необходимо задать возможности сетевого подключения. Приложению, использующему StreamSocket для подключения к сетевой службе, понадобится набор возможностей сетевого подключения.
Если приложению требуется подключаться в качестве клиента к удаленным службам в Интернете, для этого требуется возможность Интернет (клиент). Если приложению необходима возможность подключаться от имени клиента к удаленным службам в домашней или рабочей сети, то потребуется возможность Частные сети (клиент и сервер).
Если приложению требуется использовать StreamSocketListener для ожидания входящих подключений от удаленных конечных точек в Интернете, понадобится возможность Интернет (клиент и сервер) . Если приложению необходима возможность использовать StreamSocketListener для ожидания входящих подключений от удаленных конечных точек в домашней или рабочей сети, то потребуется возможность Частные сети (клиент и сервер).
Примечание В Windows Phone имеется только один тип возможности сетевого подключения — Интернет (клиент и сервер). Он обеспечивает приложению все формы доступа к сети.
Если серверный компонент этого примера, ожидающий входящих подключений, работает на том же устройстве, что и клиентский компонент, то требуется замыкание на себя. Приложения, разработанные и выполняемые в Visual Studio 2013, будут автоматически регистрироваться как освобожденные от ограничений замыкания на себя. Дополнительная информация: Как включить замыкание на себя и устранить неполадки сетевой изоляции.
Подробнее о доступе к сети: Как настроить возможности сетевой изоляции.
Эти действия нужны, чтобы настроить возможности сетевого подключения для приложения перед развертыванием, если оно обращается к веб-службе в Интернете либо в частной или рабочей сети.
Откройте файл package.appxmanifest в Microsoft Visual Studio.
Перейдите на вкладку Возможности.
Чтобы создать версию примера для Windows, выберите возможности (Интернет (клиент) и Частные сети (клиент и сервер).
Чтобы создать версию примера для Windows Phone, выберите возможность Интернет (клиент и сервер).
Сохраните и закройте файл манифеста.
Добавление пользовательского интерфейса на HTML
Откройте папку 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>
Откройте папку 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>
Откройте папку 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>
Откройте папку 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-порт) для подключения к серверу. В качестве имен узла и службы устанавливаются значения по умолчанию, которые можно изменить в пользовательском интерфейсе.
Откройте папку 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."); }
Запуск приложения
- Чтобы запустить приложение, нажмите клавишу F5 в Visual Studio, и проект будет запущен. Выберите кнопки для запуска прослушивателя, подключения к нему клиента, передачи данных и закрытия сокетов.
Краткая сводка и дальнейшие действия
В этом разделе вы создали приложение, использующее TCP-сокет потока, чтобы установить сетевое подключение и отправлять данные с помощью объекта StreamSocket. На примере этого приложения вы также увидели, как можно ожидать передачи данных TCP-подключения и принимать подключение от сокета потока, которое может использоваться для передачи и приема данных.
Исходный код и файлы сборки для этого раздела находятся в примере StreamSocket.
Вы также можете создать подключение к сети для передачи данных, используя сокет датаграмм. Пример см. в разделе Как подключаться с помощью сокета датаграмм.
Связанные разделы
Другие ресурсы
Как настроить возможности сетевого подключения
Как подключаться с помощью сокета датаграмм
Как включить замыкание на себя и выполнить отладку сетевой изоляции
Защита соединений сокетов с использованием TLS/SSL
Настройка времени ожидания для операций с сокетами
Использование расширенных элементов управления сокета
Устранение неполадок и отладка сетевых подключений
Ссылки
Windows.Storage.Stream.DataWriter
Примеры