Cómo conectarse con un socket de datagramas (HTML)
[ Este artículo está destinado a desarrolladores de Windows 8.x y Windows Phone 8.x que escriben aplicaciones de Windows en tiempo de ejecución. Si estás desarrollando para Windows 10, consulta la documentación más reciente
En este tema se muestra cómo usar UDP para enviar y recibir datos de red en una aplicación de Windows en tiempo de ejecución con DatagramSocket.
El componente cliente de la muestra crea un socket UDP, usa el socket para enviar y recibir datos y a continuación cierra el socket. El componente servidor de la muestra crea un socket UDP para escuchar paquetes de red entrantes, recibe paquetes UDP entrantes del cliente, envía datos al cliente y cierra el socket. Esta muestra se proporciona en los lenguajes de programación JavaScript, C# y C++.
El componente cliente de la muestra demuestra las siguientes funciones:
- Usa la clase DatagramSocket para crear un socket UDP para que el cliente pueda enviar y recibir datos.
- Agrega un controlador para un evento DatagramSocket.MessageReceived que indique que se recibió un datagrama UDP en el objeto DatagramSocket.
- Establece el extremo remoto de un servidor de red UDP a donde deban enviarse los paquetes mediante uno de los métodos DatagramSocket.ConnectAsync.
- Envía datos al servidor mediante el objeto Streams.DataWriter, que permite a un programador escribir tipos comunes (por ejemplo, enteros y cadenas) en cualquier secuencia.
- Cierra el socket.
El componente servidor de la muestra demuestra las siguientes funciones:
- Usa la clase DatagramSocket para crear un socket UDP para escuchar y recibir paquetes de datagramas entrantes y para enviar paquetes.
- Agrega un controlador para un evento DatagramSocket.MessageReceived que indique que se recibió un datagrama UDP en el objeto DatagramSocket.
- Enlaza el socket a un nombre de servicio local para escuchar paquetes UDP entrantes utilizando el método DatagramSocket.BindServiceNameAsync.
- Recibe un evento DatagramSocket.MessageReceived que indique que se recibió un datagrama UDP en el objeto DatagramSocket.
- Recibe datos del cliente mediante el controlador DatagramSocket.MessageReceived. El objeto DatagramSocketMessageReceivedEventArgs pasado al controlador DatagramSocket.MessageReceived permite a una aplicación recibir datos del cliente y determinar la dirección y el puerto remotos desde donde se enviaron los datos.
- Cierra el socket.
Nota Para utilizar esta muestra, se requiere acceso a la red mediante la interfaz de bucle invertido.
Objetivo: Crea una conexión de red a otro equipo o dispositivo con un socket DatagramSocket.
Requisitos previos
En los siguientes ejemplos, se usa JavaScript. Para obtener ayuda para crear tu primera aplicación, consulta Crear la primera aplicación de la Tienda Windows con JavaScript.
Para asegurarte de que la aplicación de la Tienda Windows está lista para la red, debes establecer la funcionalidad en el archivo Package.appxmanifest del proyecto. Para obtener una definición de cada funcionalidad de red, consulta el tema sobre el procedimiento para configurar las funcionalidades de aislamiento de red.
Instrucciones
1. Crear un nuevo proyecto
- Abre Microsoft Visual Studio 2013 y selecciona Nuevo proyecto en el menú Archivo.
- En la lista de plantillas, elige JavaScript.
- En la sección, elige Store apps.
- En la sección, selecciona Universal Apps, Windows apps, o Windows Phone apps (en función de la plataforma de destino) y, luego, selecciona Aplicación vacía.
- Asigna el nombre
socketsSample
a la aplicación y haz clic en Aceptar.
2. Establecer funcionalidades para habilitar el acceso a la red
Tienes que establecer las funcionalidades de red de tu aplicación si esta necesita acceso a la red. Una aplicación que utiliza un DatagramSocket para conectarse a un servicio de red necesita que se hayan establecido las funcionalidades de red.
Si la aplicación necesita conectarse a servicios remotos de Internet como cliente, establece la funcionalidad Internet (cliente). Si la aplicación necesita poder conectarse como cliente a servicios remotos en una red doméstica o de trabajo, se necesita la funcionalidad Redes privadas (cliente y servidor).
Si la aplicación necesita utilizar el DatagramSocket para escuchar las conexiones entrantes de extremos remotos en Internet, se necesita la funcionalidad Internet (cliente y servidor). Si la aplicación necesita utilizar el DatagramSocket para escuchar las conexiones entrantes de extremos remotos en una red doméstica o de trabajo, se necesita la funcionalidad Redes privadas (cliente y servidor).
Nota En Windows Phone, solo hay una funcionalidad de red Internet (cliente y servidor) que habilita todo el acceso a la red para la aplicación.
Si el componente servidor de esta muestra que escucha las conexiones entrantes se ejecuta en el mismo dispositivo que el componente cliente, sería necesario acceso de bucle invertido. Las aplicaciones que se desarrollen y se ejecuten en Visual Studio 2013 se registrarán automáticamente como exentas de las restricciones de bucle invertido. Para obtener más información, consulta Cómo habilitar el aislamiento de red de bucle invertido y de depuración.
Si quieres obtener información más detallada acerca del acceso de red, consulta Cómo configurar las funcionalidades de aislamiento de red.
Estos pasos son necesarios para establecer las funcionalidades de red de una aplicación antes de que se implemente y si la aplicación accede a un servicio de red en Internet o en una red doméstica o de trabajo.
Abre Microsoft Visual Studio para abrir el archivo package.appxmanifest.
Selecciona la pestaña Funcionalidad.
Para compilar la versión para Windows de la muestra, selecciona las funcionalidades Internet (cliente) y Redes privadas (cliente y servidor).
Para crear la versión para Windows Phone de la muestra, selecciona la funcionalidad Internet (cliente y servidor).
Guarda y cierra el archivo de manifiesto.
3. Agregar HTML UI
Abre la carpeta html. Abre un nuevo archivo startListener.html y agrega el siguiente código HTML en las secciones <head> y <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>
.
Abre la carpeta html. Abre un nuevo archivo connectToListener.html y agrega el siguiente código HTML en las secciones <head> y <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>
.
Abre la carpeta html. Abre un nuevo archivo sendData.html y agrega el siguiente código HTML en las secciones <head> y <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>
.
Abre la carpeta html. Abre un nuevo archivo closeSocket.html y agrega el siguiente código HTML en las secciones <head> y <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. Definir el ejemplo y los escenarios
En este paso, el código define el ejemplo, los archivos HTML y los escenarios que se usan en el ejemplo. El código también agrega escuchas de eventos e inicia la aplicación. Las opciones de escenario permiten al usuario iniciar la escucha de socket, iniciar el cliente para conectarse a la escucha, hacer que el cliente envíe datos al servidor y cerrar los sockets.
Abre la carpeta js. Abre el archivo default.js y agrégale el siguiente código.
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. Definir variables para funciones de eventos y sockets
En este paso, el código crea una serie de variables, incluidas el socket de escucha, el socket del cliente y otras variables para errores y eventos. Las variables se crean para realizar un seguimiento del socket del cliente y saber si está en estado conectado o en proceso de cierre. Este paso también define el nombre de host y el nombre de servicio (puerto UDP) para conectarse y enviar datos, así como el nombre de servicio local (puerto UDP) para aceptar y recibir datos. Los valores del nombre de host remoto, nombre de servicio remoto y nombre de servicio local se establecen de manera predeterminada, pero pueden cambiarse en la interfaz de usuario.
Abre la carpeta js. Abre un nuevo archivo socketsSample.js y agrégale el siguiente código.
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. Crear una escucha e iniciarla en el nombre de servicio (puerto)
El código de esta sección crea una escucha y la inicia. También se agregan funciones para controlar eventos cuando el usuario solicite que la escucha se enlace a una dirección IP y puerto UDP, acepte una conexión y lea datos enviados desde el cliente.
Nota Si bien en este ejemplo específico el cliente y el servidor están en la misma aplicación, normalmente podrías tener aplicaciones de cliente y de servidor independientes.
Abre la carpeta js. Abre un nuevo archivo startListener.js y agrégale el siguiente código:
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. Crear un socket y conectar con un extremo remoto
En este paso, el código agrega una función para crear el socket y conectar al extremo remoto, normalmente un servidor, mediante el método DatagramSocket.ConnectAsync. Se agrega una función para controlar cuándo el cliente recibe un mensaje. También se agrega una función para controlar casos en los que se presente un error cuando el cliente intente establecer una conexión.
Abre la carpeta js. Abre un nuevo archivo connectToListener.js y agrégale el siguiente código:
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. Enviar y recibir datos del cliente
En este paso, el código agrega una función para enviar datos al extremo UDP remoto mediante los métodos de la clase Windows.Storage.Streams.DataWriter.
Abre la carpeta js. Abre un nuevo archivo sendData.js y agrégale el siguiente código:
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. Cerrar los sockets
En este paso, el código cerrará los sockets mediante el método DatagramSocket.Close. Cuando los sockets se cierren, todas las operaciones pendientes terminarán y se llamarán a las rutinas de errores.
Abre la carpeta js. Abre un nuevo archivo socketClose.js y agrégale el siguiente código:
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. Ejecutar la aplicación
- Para ejecutar la aplicación, presiona F5 en Visual Studio para que se ejecute el proyecto. Selecciona los botones para iniciar la escucha, conectar el cliente a la escucha, enviar datos y cerrar los sockets.
Resumen y siguientes pasos
En este inicio rápido, creaste una aplicación que usa un socket de datagramas UDP para establecer una conexión de red y enviar datos con un objeto DatagramSocket. La aplicación también mostró cómo escuchar en un puerto UDP y recibir datos.
El código fuente y los archivos de compilación de este tema están disponibles como la muestra de DatagramSocket.
También puedes usar un socket de secuencias para que una conexión de red envíe y reciba datos. Si quieres ver un ejemplo, consulta Cómo conectar con un socket de secuencias.
Temas relacionados
Otros recursos
Procedimiento para configurar las funcionalidades de aislamiento de red
Cómo conectar con un socket de secuencias
Cómo establecer tiempos de espera en operaciones de socket
Cómo usar controles de sockets avanzados
Solucionar problemas y depurar conexiones de red
Referencia
Muestras