Escribir código que se ejecute en un dispositivo determinado es muy satisfactorio. Pero escribir código que se ejecuta en varios dispositivos que se comunican entre sí es simplemente una afirmación de la vida. Este artículo le enseñará cómo conectarse e intercambiar mensajes a través de la red utilizando el protocolo de control de transmisión (TCP).
En este artículo, configurará una aplicación que conectará su computadora consigo misma y, esencialmente, la volverá loca: hablar consigo misma. También aprenderá la diferencia entre los dos flujos más utilizados para redes en Java y cómo funcionan.
Flujos de datos y objetos
Antes de sumergirse en el código, es necesario distinguir la diferencia entre las dos corrientes utilizadas en el artículo.
Flujos de datos
Los flujos de datos procesan cadenas y tipos de datos primitivos. Los datos enviados a través de flujos de datos deben serializarse y deserializarse manualmente, lo que dificulta la transferencia de datos complejos. Pero los flujos de datos pueden comunicarse con servidores y clientes escritos en otros lenguajes además de Java. Los flujos sin procesar son similares a los flujos de datos en ese aspecto, pero los flujos de datos garantizan que los datos se formatee de una manera independiente de la plataforma, lo cual es beneficioso porque ambas partes podrán leer los datos enviados.
Flujos de objetos
Los flujos de objetos procesan tipos de datos primitivos y objetos que implementan
Serializable
interfaz. Los datos enviados a través de secuencias de objetos se serializan y deserializan automáticamente, lo que facilita la transferencia de datos complejos. Pero, los flujos de objetos solo pueden comunicarse con servidores y clientes escritos en Java. También,
ObjectOutputStream
tras la inicialización, envía un encabezado al
Flujo de entrada
de la otra parte que, tras la inicialización, bloquea la ejecución hasta que se recibe el encabezado.
Pasos
Paso 1. Crea una clase
Crea una clase y nómbrala como quieras. En este artículo, se llamará
NetworkAppExample
clase pública NetworkAppExample {}
Paso 2. Cree un método principal
Cree un método principal y declare que podría generar excepciones de
Excepción
type y cualquier subclase del mismo - todas las excepciones. Esto se considera una mala práctica, pero es aceptable para ejemplos básicos.
public class NetworkAppExample {public static void main (String args) throws Exception {}}
Paso 3. Declare la dirección del servidor
Este ejemplo utilizará la dirección de host local y un número de puerto arbitrario. El número de puerto debe estar en un rango de 0 a 65535 (inclusive). Sin embargo, los números de puerto que se deben evitar van de 0 a 1023 (inclusive) porque son puertos del sistema reservados.
public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; }}
Paso 4. Cree un servidor
El servidor está vinculado a la dirección y al puerto y escucha las conexiones entrantes. En Java,
ServerSocket
representa el punto final del lado del servidor y su función es aceptar nuevas conexiones.
ServerSocket
no tiene flujos para leer y enviar datos porque no representa una conexión entre un servidor y un cliente.
import java.net. InetAddress; import java.net. ServerSocket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); }}
Paso 5. Iniciar sesión en el servidor
Para fines de registro, imprima en la consola que el servidor se ha iniciado.
import java.net. InetAddress; import java.net. ServerSocket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); }}
Paso 6. Cree un cliente
El cliente está vinculado a la dirección y al puerto de un servidor y escucha los paquetes (mensajes) después de que se establece la conexión. En Java,
Enchufe
representa un punto final del lado del cliente conectado al servidor o una conexión (desde el servidor) al cliente y se utiliza para comunicarse con la parte en el otro extremo.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); }}
Paso 7. Registre el intento de conexión
Para fines de registro, imprima en la consola que se ha intentado la conexión.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); }}
Paso 8. Establezca la conexión
Los clientes nunca se conectarán a menos que el servidor escuche y acepte, en otras palabras, establezca conexiones. En Java, las conexiones se establecen utilizando
aceptar()
método de
ServerSocket
clase. El método bloqueará la ejecución hasta que un cliente se conecte.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); }}
Paso 9. Registre la conexión establecida
Para fines de registro, imprima en la consola que se ha establecido la conexión entre el servidor y el cliente.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); }}
Paso 10. Prepare los flujos de comunicación
La comunicación se realiza a través de flujos y, en esta aplicación, los flujos sin procesar del (conexión desde) el servidor (al cliente) y el cliente deben estar encadenados a flujos de datos o de objetos. Recuerde, ambas partes deben utilizar el mismo tipo de transmisión.
-
Flujos de datos
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); DataOutputStream clientOut = nuevo DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nuevo DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nuevo DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nuevo DataInputStream (connection.getInputStream ()); }}
-
Flujos de objetos
Cuando se utilizan múltiples flujos de objetos, los flujos de entrada deben inicializarse en el mismo orden que los flujos de salida porque
ObjectOutputStream
envía un encabezado a la otra parte y
ObjectInputStream
bloquea la ejecución hasta que lee el encabezado.
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (conexión.getInputStream ()); }}
El orden como se especifica en el código anterior puede ser más fácil de recordar: primero inicialice los flujos de salida y luego los flujos de entrada en el mismo orden. Sin embargo, otro orden para la inicialización de flujos de objetos es el siguiente:
ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (connection.getInputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ());
Paso 11. Registre que la comunicación está lista
Para fines de registro, imprima en la consola que la comunicación está lista.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); // código omitido System.out.println ("La comunicación está lista"); }}
Paso 12. Crea un mensaje
En esta aplicación,
Hola Mundo
El texto se enviará al servidor como
byte
o
Cuerda
. Declare una variable del tipo que depende del flujo utilizado. Usar
byte
para flujos de datos y
Cuerda
para flujos de objetos.
-
Flujos de datos
Utilizando flujos de datos, la serialización se realiza convirtiendo objetos en tipos de datos primitivos o un
Cuerda
. En este caso,
Cuerda
se convierte en
byte
en lugar de escribir usando
writeBytes ()
método para mostrar cómo se haría con otros objetos, como imágenes u otros archivos.
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); DataOutputStream clientOut = nuevo DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nuevo DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nuevo DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nuevo DataInputStream (connection.getInputStream ()); System.out.println ("La comunicación está lista"); byte messageOut = "Hola mundo".getBytes (); }}
-
Flujos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (conexión.getInputStream ()); System.out.println ("La comunicación está lista"); String messageOut = "Hola mundo"; }}
Paso 13. Envíe el mensaje
Escriba datos en el flujo de salida y elimine el flujo para asegurarse de que los datos se hayan escrito por completo.
-
Flujos de datos
La longitud de un mensaje debe enviarse primero para que la otra parte sepa cuántos bytes necesita leer. Una vez que la longitud se envía como un tipo de entero primitivo, se pueden enviar bytes.
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); DataOutputStream clientOut = nuevo DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nuevo DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nuevo DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nuevo DataInputStream (connection.getInputStream ()); System.out.println ("La comunicación está lista"); byte messageOut = "Hola mundo".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); }}
-
Flujos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (connection.getInputStream ()); System.out.println ("La comunicación está lista"); String messageOut = "Hola mundo"; clientOut.writeObject (messageOut); clientOut.flush (); }}
Paso 14. Registre el mensaje enviado
Para fines de registro, imprima en la consola el mensaje que se envió.
-
Flujos de datos
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); DataOutputStream clientOut = nuevo DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nuevo DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nuevo DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nuevo DataInputStream (connection.getInputStream ()); System.out.println ("La comunicación está lista"); byte messageOut = "Hola mundo".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Mensaje enviado al servidor:" + nueva Cadena (messageOut)); }}
-
Flujos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (conexión.getInputStream ()); System.out.println ("La comunicación está lista"); String messageOut = "Hola mundo"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Mensaje enviado al servidor:" + messageOut); }}
Paso 15. Lea el mensaje
Lea los datos del flujo de entrada y conviértalos. Como sabemos exactamente el tipo de datos enviados, crearemos un
Cuerda
de
byte
o emitir
Objeto
para
Cuerda
sin comprobar, dependiendo de la secuencia utilizada.
-
Flujos de datos
Como la longitud se envió primero y los bytes después, la lectura debe realizarse en el mismo orden. En caso de que la longitud sea cero, no hay nada que leer. El objeto se deserializa cuando los bytes se vuelven a convertir en una instancia, en este caso, de
Cuerda
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); DataOutputStream clientOut = nuevo DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nuevo DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nuevo DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nuevo DataInputStream (connection.getInputStream ()); System.out.println ("La comunicación está lista"); byte messageOut = "Hola mundo".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Mensaje enviado al servidor:" + nueva Cadena (messageOut)); int longitud = serverIn.readInt (); if (longitud> 0) {byte mensajeIn = nuevo byte [longitud]; serverIn.readFully (messageIn, 0, messageIn.length); }}}
-
Flujos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (conexión.getInputStream ()); System.out.println ("La comunicación está lista"); String messageOut = "Hola mundo"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Mensaje enviado al servidor:" + messageOut); String messageIn = (String) serverIn.readObject (); }}
Paso 16. Registrar mensaje leído
Para fines de registro, imprima en la consola el mensaje que se recibió e imprima su contenido.
-
Flujos de datos
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); DataOutputStream clientOut = nuevo DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nuevo DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nuevo DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nuevo DataInputStream (connection.getInputStream ()); System.out.println ("La comunicación está lista"); byte messageOut = "Hola mundo".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Mensaje enviado al servidor:" + nueva Cadena (messageOut)); int longitud = serverIn.readInt (); if (longitud> 0) {byte mensajeIn = nuevo byte [longitud]; serverIn.readFully (messageIn, 0, messageIn.length); System.out.println ("Mensaje recibido del cliente:" + nueva Cadena (messageIn)); }}}
-
Flujos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); ObjectOutputStream clientOut = nuevo ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nuevo ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nuevo ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nuevo ObjectInputStream (conexión.getInputStream ()); System.out.println ("La comunicación está lista"); String messageOut = "Hola mundo"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Mensaje enviado al servidor:" + messageOut); String messageIn = (String) serverIn.readObject (); System.out.println ("Mensaje recibido del cliente:" + messageIn); }}
Paso 17. Desconecte las conexiones
La conexión se desconecta cuando una de las partes cierra sus transmisiones. En Java, al cerrar el flujo de salida, el conector asociado y el flujo de entrada también se cierran. Una vez que una parte del otro extremo descubre que la conexión está muerta, también debe cerrar su flujo de salida para evitar pérdidas de memoria.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); // código omitido System.out.println ("La comunicación está lista"); // código omitido clientOut.close (); serverOut.close (); }}
Paso 18. Desconexión del registro
Para fines de registro, se han desconectado las conexiones de impresión a la consola.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); // código omitido System.out.println ("La comunicación está lista"); // código omitido clientOut.close (); serverOut.close (); System.out.println ("Conexiones cerradas"); }}
Paso 19. Termine el servidor
Las conexiones están desconectadas, pero el servidor aún está funcionando. Como
ServerSocket
no está asociado con ninguna secuencia, debe cerrarse explícitamente llamando
cerrar()
método.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); // código omitido System.out.println ("La comunicación está lista"); // código omitido clientOut.close (); serverOut.close (); System.out.println ("Conexiones cerradas"); server.close (); }}
Paso 20. Terminación del servidor de registros
Para fines de registro, la impresión en el servidor de la consola ha finalizado.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; ServerSocket servidor = nuevo ServerSocket (puerto, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado"); Cliente de socket = nuevo Socket (host, puerto); System.out.println ("Conectando al servidor …"); Conexión de socket = server.accept (); System.out.println ("Conexión establecida"); // código omitido System.out.println ("La comunicación está lista"); // código omitido clientOut.close (); serverOut.close (); System.out.println ("Conexiones cerradas"); server.close (); System.out.println ("Servidor terminado"); }}
Paso 21. Compile y ejecute
El registro nos permitió saber si la aplicación fue exitosa o no. Rendimiento esperado:
Se inició el servidor. Conectando al servidor… Conexión establecida. La comunicación está lista. Mensaje enviado al servidor: Hello World Mensaje recibido del cliente: Hello World Connections cerradas. Servidor terminado.
En caso de que su salida no sea como la anterior, lo que es poco probable que suceda, existen algunas soluciones:
-
Si la salida se detiene en la línea
Conexión establecida.
y se utilizan flujos de objetos, vaciar cada
ObjectOutputStream
- inmediatamente después de la inicialización porque los encabezados, por alguna razón, no se enviaron.
-
Si la salida se imprime
java.net. BindException: dirección ya en uso
- elija un número de puerto diferente porque el especificado ya está en uso.
Consejos
- La conexión a un servidor en una red diferente se realiza mediante la conexión a la dirección IP externa de un dispositivo que ejecuta el servidor que tiene un puerto reenviado.
- La conexión a un servidor en la misma red se realiza conectándose a la dirección IP privada de un dispositivo que ejecuta el servidor o reenviando un puerto y conectándose a la dirección IP externa del dispositivo.
- Existen software, como Hamachi, que permiten conectarse al servidor en una red diferente sin reenviar un puerto, pero requiere la instalación del software en ambos dispositivos.
Ejemplos de
Las aplicaciones de red que utilizan el bloqueo de entrada / salida deben utilizar subprocesos. Los siguientes ejemplos muestran una implementación minimalista de servidor y cliente con subprocesos. El código de red es esencialmente el mismo que en el artículo, excepto que algunos fragmentos se sincronizaron, se movieron a subprocesos y se manejan las excepciones.
Server.java
import java.io. IOException; import java.net. InetAddress; import java.net. ServerSocket; import java.net. SocketException; import java.net. UnknownHostException; import java.util. ArrayList; import java.util. Collections; import java.util. List; / ** * La clase {@code Server} representa un punto final de servidor en una red. {@code Server}, una vez vinculado a una determinada dirección IP * y puerto, establece conexiones con los clientes y puede comunicarse con ellos o desconectarlos. *
* Esta clase es segura para subprocesos. * * @version 1.0 * @see Client * @see Connection * / public class Server implementa Runnable {servidor ServerSocket privado; lista privada
conexiones; hilo de hilo privado; Objeto final privado connectionsLock = new Object (); / ** * Construye un {@code Server} que interactúa con los clientes en el puerto y el nombre de host especificados con la longitud máxima especificada * solicitada de una cola de clientes entrantes. * * @param host Dirección de host a utilizar. * @param port Número de puerto a utilizar. * @param backlog Longitud máxima solicitada de la cola de clientes entrantes. * @throws NetworkException Si se produce un error al iniciar un servidor. * / public Server (String host, int port, int backlog) lanza NetworkException {try {server = new ServerSocket (puerto, backlog, InetAddress.getByName (host)); } catch (UnknownHostException e) {throw new NetworkException ("No se pudo resolver el nombre del host:" + host, e); } catch (IllegalArgumentException e) {throw new NetworkException ("El número de puerto debe estar entre 0 y 65535 (inclusive):" + puerto); } catch (IOException e) {lanzar nueva NetworkException ("No se pudo iniciar el servidor", e); } conexiones = Collections.synchronizedList (new ArrayList ()); hilo = nuevo hilo (esto); thread.start (); } / ** * Construye un {@code Server} que interactúa con los clientes en el puerto y el nombre de host especificados. * * @param host Dirección de host para enlazar. * @param port Número de puerto para enlazar. * @throws NetworkException Si se producen errores al iniciar un servidor. * / public Server (String host, int port) lanza NetworkException {this (host, puerto, 50); } / ** * Escucha, acepta y registra las conexiones entrantes de los clientes. * / @Override public void run () {while (! Server.isClosed ()) {try {connections.add (new Connection (server.accept ())); } catch (SocketException e) {if (! e.getMessage (). equals ("Socket cerrado")) {e.printStackTrace (); }} catch (NetworkException | IOException e) {e.printStackTrace (); }}} / ** * Envía datos a todos los clientes registrados. * * @param data Datos a enviar. * @throws IllegalStateException Si se intenta escribir datos cuando el servidor está fuera de línea. * @throws IllegalArgumentException Si los datos a enviar son nulos. * / public void broadcast (datos del objeto) {if (server.isClosed ()) {throw new IllegalStateException ("Datos no enviados, el servidor está desconectado"); } if (data == null) {lanzar nueva IllegalArgumentException ("datos nulos"); } sincronizado (connectionsLock) {para (Conexión conexión: conexiones) {prueba {conexión.enviar (datos); System.out.println ("Datos enviados al cliente correctamente"); } captura (NetworkException e) {e.printStackTrace (); }}}} / ** * Envía un mensaje de desconexión y desconecta al cliente especificado. * * @param connection Cliente para desconectar. * @throws NetworkException Si se produce un error al cerrar la conexión. * / public void desconectar (Conexión de conexión) lanza NetworkException {if (connections.remove (connection)) {connection.close (); }} / ** * Envía un mensaje de desconexión a todos los clientes, los desconecta y finaliza el servidor. * / public void close () lanza NetworkException {sincronizado (connectionsLock) {para (Conexión conexión: conexiones) {prueba {conexión.close (); } captura (NetworkException e) {e.printStackTrace (); }}} connections.clear (); intente {servidor.close (); } catch (IOException e) {lanzar una nueva NetworkException ("Error al cerrar el servidor"); } finalmente {thread.interrupt (); }} / ** * Devuelve si el servidor está en línea o no. * * @return True si el servidor está en línea. Falso, de lo contrario. * / public boolean isOnline () {return! server.isClosed (); } / ** * Devuelve una matriz de clientes registrados. * / conexión pública getConnections () {sincronizado (connectionsLock) {return connections.toArray (nueva conexión [connections.size ()]); }}}
Client.java
import java.io. IOException; import java.net. Socket; import java.net. UnknownHostException; / ** * La clase {@code Client} representa un punto final de cliente en una red. Se garantiza que {@code Client}, una vez conectado a un determinado * servidor, solo podrá comunicarse con el servidor. Si otros clientes reciben o no los datos * depende de la implementación del servidor. *
* Esta clase es segura para subprocesos. * * @version 1.0 * @see Server * @see Connection * / public class Client {conexión de conexión privada; / ** * Construye un {@code Client} conectado al servidor en el host y puerto especificados. * * @param host Dirección de host para enlazar. * @param port Número de puerto para enlazar. * @throws NetworkException Si se produce un error al iniciar un servidor. * / public Client (String host, int port) lanza NetworkException {try {connection = new Connection (new Socket (host, port)); } catch (UnknownHostException e) {throw new NetworkException ("No se pudo resolver el nombre del host:" + host, e); } catch (IllegalArgumentException e) {throw new NetworkException ("El número de puerto debe estar entre 0 y 65535 (inclusive):" + puerto); } catch (IOException e) {lanzar una nueva NetworkException ("No se pudo iniciar el servidor", e); }} / ** * Envía datos a la otra parte. * * @param data Datos a enviar. * @throws NetworkException Si falla la escritura en el flujo de salida. * @throws IllegalStateException Si se intenta escribir datos cuando se cierra la conexión. * @throws IllegalArgumentException Si los datos a enviar son nulos. * @throws UnsupportedOperationException Si se intenta enviar un tipo de datos no admitido. * / public void send (datos del objeto) lanza NetworkException {connection.send (datos); } / ** * Envía un mensaje de desconexión y cierra la conexión con el servidor. * / public void close () lanza NetworkException {connection.close (); } / ** * Devuelve si el cliente está conectado al servidor o no. * * @return True si el cliente está conectado. Falso, de lo contrario. * / public boolean isOnline () {return connection.isConnected (); } / ** * Devuelve la instancia de {@link Connection} del cliente. * / public Connection getConnection () {conexión de retorno; }}
Connection.java
import java.io. DataInputStream; import java.io. DataOutputStream; import java.io. IOException; import java.net. Socket; import java.net. SocketException; / ** * La clase {@code Connection} representa una conexión del servidor al cliente o un punto final del cliente en una red * {@code Connection}, una vez conectado, puede intercambiar datos con otras partes, según en una implementación de servidor *. *
* Esta clase es segura para subprocesos. * * @version 1.0 * @see Server * @see Client * / clase pública Connection implementa Runnable {private Socket socket; salida de DataOutputStream privada; Private DataInputStream en; hilo de hilo privado; Objeto final privado writeLock = new Object (); Objeto final privado readLock = new Object (); / ** * Construye {@code Connection} usando transmisiones de un {@link Socket} especificado. * * @param socket Socket del que se obtienen las transmisiones.* / public Connection (Socket socket) lanza NetworkException {if (socket == null) {throw new IllegalArgumentException ("socket nulo"); } this.socket = socket; probar {fuera = nuevo DataOutputStream (socket.getOutputStream ()); } catch (IOException e) {lanzar nueva NetworkException ("No se pudo acceder al flujo de salida.", e); } try {in = new DataInputStream (socket.getInputStream ()); } catch (IOException e) {lanzar nueva NetworkException ("No se pudo acceder al flujo de entrada.", e); } hilo = nuevo hilo (esto); thread.start (); } / ** * Lee mensajes mientras la conexión con la otra parte está activa. * / @Override public void run () {while (! Socket.isClosed ()) {try {int identifier; byte bytes; sincronizado (readLock) {identificador = in.readInt (); int length = in.readInt (); if (longitud> 0) {bytes = nuevo byte [longitud]; in.readFully (bytes, 0, bytes.length); } else {continuar; }} interruptor (identificador) {identificador de caso. INTERNA: comando de cadena = nueva cadena (bytes); if (command.equals ("desconectar")) {if (! socket.isClosed ()) {System.out.println ("Paquete de desconexión recibido."); intente {cerrar (); } captura (NetworkException e) {return; } } } rotura; case Identifier. TEXT: System.out.println ("Mensaje recibido:" + nueva cadena (bytes)); rotura; predeterminado: System.out.println ("Se recibieron datos no reconocidos"); }} catch (SocketException e) {if (! e.getMessage (). equals ("Socket cerrado")) {e.printStackTrace (); }} catch (IOException e) {e.printStackTrace (); }}} / ** * Envía datos a la otra parte. * * @param data Datos a enviar. * @throws NetworkException Si falla la escritura en el flujo de salida. * @throws IllegalStateException Si se intenta escribir datos cuando se cierra la conexión. * @throws IllegalArgumentException Si los datos a enviar son nulos. * @throws UnsupportedOperationException Si se intenta enviar un tipo de datos no admitido. * / public void send (datos de objeto) lanza NetworkException {if (socket.isClosed ()) {lanza nueva IllegalStateException ("Datos no enviados, conexión cerrada."); } if (data == null) {lanzar nueva IllegalArgumentException ("datos nulos"); } int identifier; byte bytes; if (instancia de datos de String) {identifier = Identifier. TEXT; bytes = ((Cadena) datos).getBytes (); } else {lanzar nueva UnsupportedOperationException ("Tipo de datos no admitido:" + data.getClass ()); } probar {sincronizado (bloqueo de escritura) {out.writeInt (identificador); out.writeInt (bytes.length); out.write (bytes); out.flush (); }} catch (IOException e) {lanzar nueva NetworkException ("No se pudieron enviar los datos", e); }} / ** * Envía un mensaje de desconexión y cierra la conexión con la otra parte. * / public void close () lanza NetworkException {if (socket.isClosed ()) {throw new IllegalStateException ("La conexión ya está cerrada."); } intente {byte mensaje = "desconectar".getBytes (); sincronizado (bloqueo de escritura) {out.writeInt (Identificador. INTERNAL); out.writeInt (mensaje.longitud); out.write (mensaje); out.flush (); }} catch (IOException e) {System.out.println ("No se pudo enviar el mensaje de desconexión"); } try {sincronizado (writeLock) {out.close (); }} catch (IOException e) {lanzar una nueva NetworkException ("Error al cerrar la conexión", e); } finalmente {thread.interrupt (); }} / ** * Devuelve si la conexión con la otra parte está activa o no. * * @return True si la conexión está activa. Falso, de lo contrario. * / public boolean isConnected () {return! socket.isClosed (); }}
Identificador.java
/ ** * La clase {@code Identifier} contiene constantes que usa {@link Connection} para serializar y deserializar los datos * enviados a través de la red. * * @version 1.0 * @see Connection * / public final class Identifier {/ ** * Identificador para mensajes internos. * / public static final int INTERNAL = 1; / ** * Identificador para mensajes de texto. * / public static final int TEXT = 2; }
NetworkException.java
/ ** * La clase {@code NetworkException} indica un error relacionado con la red. * / public class NetworkException extiende la excepción {/ ** * Construye una {@code NetworkException} con {@code null} como su mensaje. * / public NetworkException () {} / ** * Construye una {@code NetworkException} con el mensaje especificado. * * @param message Un mensaje para describir el error. * / public NetworkException (String mensaje) {super (mensaje); } / ** * Construye una {@code NetworkException} con el mensaje y la causa especificados. * * @param message Un mensaje para describir el error. * @param cause Una causa de error. * / public NetworkException (mensaje de cadena, causa descartable) {super (mensaje, causa); } / ** * Construye una {@code NetworkException} con la causa especificada. * * @param cause Una causa de error. * / public NetworkException (causa descartable) {super (causa); }}
UsageExample.java
/ ** * La clase {@code UsageExample} muestra el uso de {@link Server} y {@link Client}. Este ejemplo usa * {@link Thread # sleep (long)} para garantizar que cada segmento se ejecute porque el inicio y el cierre rápidos hacen que algunos * segmentos no se ejecuten. * * @version 1.0 * @see Server * @see Client * / public class UsageExample {public static void main (String args) throws Exception {String host = "localhost"; int puerto = 10430; Servidor servidor = nuevo servidor (host, puerto); Cliente cliente = nuevo Cliente (host, puerto); Thread.sleep (100L); client.send ("Hola."); server.broadcast ("¡Hola, amigo!"); Thread.sleep (100L); server.disconnect (server.getConnections () [0]); // o client.close () para desconectarse del server.close () del lado del cliente; }}