JavaRush /Blog Java /Random-ES /Las clases Socket y ServerSocket, o “¿Hola, servidor? ¿Pu...
Sergey Simonov
Nivel 36
Санкт-Петербург

Las clases Socket y ServerSocket, o “¿Hola, servidor? ¿Puedes oírme?"

Publicado en el grupo Random-ES
Introducción: “Había una computadora sobre la mesa, detrás de ella había un codificador...” Las clases Socket y ServerSocket, o “¿Hola, servidor?  ¿Puedes oírme?"  - 1 Érase una vez, uno de mis compañeros publicó otro resultado de su estudio de Java, en forma de una captura de pantalla de un nuevo programa. Este programa era un chat multiusuario. En ese momento yo apenas estaba comenzando mi propio camino hacia el dominio de la programación en este lenguaje, pero definitivamente me di cuenta de que “¡lo quiero!” Pasó el tiempo y, habiendo terminado de trabajar en el siguiente proyecto como parte de profundizar mis conocimientos de programación, recordé ese incidente y decidí que era el momento. De alguna manera ya comencé a profundizar en este tema por pura curiosidad, pero en mi libro de texto principal de Java (era el manual completo de Schildt) solo se proporcionaban 20 páginas para el paquete java.net. Esto es comprensible: el libro ya es muy extenso. Había tablas de métodos y constructores de las clases principales, pero eso es todo. El siguiente paso es, por supuesto, el todopoderoso Google: innumerables artículos diferentes donde se presenta lo mismo: dos o tres palabras sobre enchufes y un ejemplo ya preparado. El enfoque clásico (al menos en mi estilo de estudio) es primero entender qué necesito de las herramientas para el trabajo, qué son, por qué son necesarias, y solo entonces, si la solución al problema no es obvia, elegir los listados ya hechos, desenroscando las tuercas y tornillos. Pero descubrí qué era qué y finalmente escribí un chat multiusuario. Exteriormente, resultó algo como esto: Las clases Socket y ServerSocket, o “¿Hola, servidor?  ¿Puedes oírme?"  - 2Aquí intentaré brindarle una comprensión de los conceptos básicos de las aplicaciones cliente-servidor basadas en sockets Java usando el ejemplo del diseño de chat. En el curso de Javarash chatearás. Estará en un nivel completamente diferente, hermoso, grande y multifuncional. Pero antes que nada, siempre es necesario sentar las bases, por lo que aquí debemos descubrir qué subyace a dicha sección. (Si encuentra alguna deficiencia o error, escriba en un MP o en un comentario debajo del artículo). Vamos a empezar. Cabeza uno: "La casa que..." Para explicar cómo se produce una conexión de red entre un servidor y un cliente, tomemos el ejemplo ya clásico de un edificio de apartamentos. Digamos que un cliente necesita establecer de alguna manera una conexión con un servidor específico. ¿Qué necesita saber el buscador sobre el objeto de búsqueda? Sí, la dirección. El servidor no es una entidad mágica en la nube y, por tanto, debe estar ubicado en una máquina específica. Por analogía con una casa, donde debe tener lugar una reunión de dos partes acordadas. Y para encontrarse en un edificio de apartamentos, no basta con una dirección del edificio, es necesario indicar el número del apartamento en el que se realizará el encuentro. Asimismo, en una computadora puede haber varios servidores a la vez, y para que el cliente se comunique con uno específico, también debe especificar el número de puerto a través del cual se realizará la conexión. Entonces, la dirección y el número de puerto. Una dirección significa un identificador de una máquina en el espacio de Internet. Puede ser un nombre de dominio, por ejemplo, "javarush.ru" o una IP normal. Puerto- un número único al que está asociado un socket específico (este término se analizará más adelante), es decir, lo ocupa un determinado servicio para que pueda utilizarse para contactar con él. Entonces, para que al menos dos objetos se encuentren en el territorio de uno (servidor), el propietario del área (servidor) debe ocupar un apartamento (puerto) específico (automóvil) en él, y el segundo debe encontrar el lugar de encuentro sabiendo la dirección de la casa (dominio o ip) y el número de apartamento (puerto). Cabeza dos: Conozca Socket Entre los conceptos y términos relacionados con el trabajo en la red, uno muy importante es Socket. Denota el punto a través del cual se produce la conexión. En pocas palabras, un socket conecta dos programas en una red. La clase Socketimplementa la idea de un socket. El cliente y el servidor se comunicarán a través de sus canales de entrada/salida: Las clases Socket y ServerSocket, o “¿Hola, servidor?  ¿Puedes oírme?"  - 3 Esta clase se declara en el lado del cliente y el servidor la recrea recibiendo una señal de conexión. Así funciona la comunicación online. Para empezar, aquí están los posibles constructores de clases Socket:
Socket(String Nombre_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-DIRECCIÓN, int порт) throws UnknownHostException
“nombre_host”: implica un nodo de red específico, una dirección IP. Si la clase de socket no pudo convertirla a una dirección real existente, se generará una excepción UnknownHostException. El puerto es un puerto. Si se especifica 0 como número de puerto, el propio sistema asignará un puerto libre. También puede ocurrir una excepción si se pierde la conexión IOException. Cabe señalar que el tipo de dirección en el segundo constructor es InetAddress. Viene al rescate, por ejemplo, cuando necesita especificar un nombre de dominio como dirección. Además, cuando un dominio significa varias direcciones IP, InetAddresspuede usarlas para obtener una serie de ellas. Sin embargo, también funciona con IP. También puede obtener el nombre del host, la matriz de bytes que constituye la dirección IP, etc. Lo abordaremos un poco más, pero tendrás que consultar la documentación oficial para obtener todos los detalles. Cuando se inicializa un objeto de tipo Socket, el cliente al que pertenece anuncia en la red que quiere conectarse al servidor en una dirección y número de puerto específicos. A continuación se muestran los métodos de la clase más utilizados Socket: InetAddress getInetAddress()– devuelve un objeto que contiene datos sobre el socket. Si el socket no está conectado - nulo int getPort()- devuelve el puerto en el que se produce la conexión al servidor int getLocalPort()- devuelve el puerto al que está vinculado el socket. El hecho es que el cliente y el servidor pueden "comunicarse" en un puerto, pero los puertos a los que están vinculados pueden ser completamente diferentes boolean isConnected()- devuelve verdadero si se establece la conexión void connect(SocketAddress DIRECCIÓN)- indica una nueva conexión boolean isClosed()- devuelve verdadero si el socket está cerrado boolean isBound()- devuelve verdadero, si el socket está realmente vinculado a una dirección, la clase Socketimplementa la interfaz AutoCloseable, por lo que puede usarse en el archivo try-with-resources. Sin embargo, también puedes cerrar un socket de la forma clásica, usando close(). Cabeza tres: y este es un ServerSocket Digamos que declaramos, en forma de clase Socket, una solicitud de conexión en el lado del cliente. ¿Cómo adivinará el servidor nuestro deseo? Para esto, el servidor tiene una clase como ServerSockety el método aceptar() en ella. Sus constructores se presentan a continuación:
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_DIRECCIÓN) throws IOException
Al declarar, ServerSocket no es necesario especificar la dirección de conexión, ya que la comunicación se realiza en la máquina del servidor. Sólo con un host multicanal es necesario especificar a qué IP está vinculado el socket del servidor. Cabeza tres. Uno: El servidor que dice no Dado que proporcionar a un programa más recursos de los que necesita es costoso e irrazonable, por lo tanto, en el constructor se ServerSocketle pide que declare las conexiones máximas aceptadas por el servidor durante la operación. Si no se especifica, de forma predeterminada este número se considerará igual a 50. Sí, en teoría podemos suponer que ServerSocketse trata del mismo socket, solo que para el servidor. Pero juega un papel completamente diferente al de la clase Socket. Sólo es necesario en la etapa de creación de la conexión. Después de crear un objeto de tipo, ServerSocketdebe saber que alguien quiere conectarse al servidor. El método aceptar() está conectado aquí. El objetivo espera hasta que alguien quiera conectarse a él y, cuando esto sucede, devuelve un objeto de tipo Socket, es decir, un socket de cliente recreado. Y ahora que se ha creado el socket del cliente en el lado del servidor, puede comenzar la comunicación bidireccional. Crear un objeto de tipo Socketen el lado del cliente y recrearlo usando ServerSocketel lado del servidor es el mínimo requerido para la conexión. Cabeza cuatro: Carta a Papá Noel Вопрос: ¿Cómo se comunican exactamente el cliente y el servidor? Ответ:A través de flujos de E/S. ¿Qué tenemos ya? Un socket con la dirección del servidor y el número de puerto del cliente, y lo mismo, gracias a Accept(), del lado del servidor. Por tanto, es razonable suponer que se comunicarán a través de un socket. Para ello, existen dos métodos que dan acceso a flujos InputStreamy OutputStreamobjetos de tipo Socket. Aquí están:
InputStream getInputStream()
OutputStream getOutputStream()
Dado que leer y escribir bytes básicos no es tan eficiente, las transmisiones se pueden empaquetar en clases de adaptador, almacenadas en búfer o no. Por ejemplo:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Para que la comunicación sea bidireccional, dichas operaciones deben realizarse en ambos lados. Ahora puedes enviar algo usando y recibir algo usando, y viceversa. En realidad, esta es prácticamente la única función de la clase Socket. Y sí, no te olvides del método Flush() BufferedWriter: vacía el contenido del buffer. Si no se hace así, la información no será transmitida y, por tanto, no será recibida. El hilo receptor también espera el indicador de fin de línea – “\n”, de lo contrario el mensaje no será aceptado, ya que en realidad el mensaje no está completo y no está completo. Si esto le parece un inconveniente, no se preocupe, siempre puede usar la clase PrintWriter, que debe completarse, especificar verdadero como segundo argumento y luego la salida del búfer se producirá automáticamente:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Además, no es necesario indicar el final de la línea; esta clase lo hace por usted. Pero, ¿es la E/S de cadena el límite de lo que puede hacer un socket? No, ¿quieres enviar objetos a través de flujos de socket? Por el amor de Dios. Serialízalos y listo:
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Cabeza cinco: comunicación real a través de Internet, ya que para conectarse a través de una red real con una dirección IP real, necesita tener un servidor completo, y desde:
  1. Nuestro chat futuro, como utilidad, no tiene tales capacidades. Sólo puede establecer una conexión y recibir/enviar un mensaje. Es decir, no tiene capacidades de servidor reales.
  2. Nuestro servidor, que contiene sólo datos de socket y flujos de E/S, no puede funcionar como un servidor WEB o FTP real, entonces solo con esto no podremos conectarnos a Internet.
Y además, apenas estamos comenzando a desarrollar el programa, lo que significa que no es lo suficientemente estable para trabajar inmediatamente con una red real, por lo que usaremos el host local como dirección de conexión. Es decir, en teoría, el cliente y el servidor todavía no estarán conectados de ninguna manera excepto a través de un socket, pero para depurar el programa estarán en la misma máquina, sin contacto real a través de la red. Para indicar en el constructor Socketque la dirección es local, existen 2 formas:
  1. Escriba "localhost" como argumento de dirección, es decir, un código auxiliar local. "127.0.0.1" también es adecuado para esto: es solo una forma digital de un talón.
  2. Usando InetAddress:
    1. InetAddress.getByName(null)- puntos nulos para localhost
    2. InetAddress.getByName("localhost")
    3. InetAddress.getByName("127.0.0.1")
Para simplificar, usaremos "localhost" de tipo String. Pero todas las demás opciones también son viables. Cabeza seis: Es hora de conversar. Entonces, ya tenemos todo lo que necesitamos para implementar una sesión de conversación con el servidor. Todo lo que queda es armarlo: el siguiente listado muestra cómo el cliente se conecta al servidor, le envía un mensaje y el servidor, a su vez, confirma que recibió el mensaje usándolo como argumento en su: "Servidor. Java"
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    private static Socket clientSocket; // conector para comunicación
    private static ServerSocket server; // enchufe del servidor
    private static BufferedReader in; // flujo de lectura de socket
    private static BufferedWriter out; // flujo de escritura de socket

    public static void main(String[] args) {
        try {
            try  {
                server = new ServerSocket(4004); // socket del servidor escuchando en el puerto 4004
                System.out.println("¡El servidor se está ejecutando!"); // el servidor estaría bien
                // anuncia tu lanzamiento
                clientSocket = server.accept(); // accept() esperará hasta
                //alguien no querrá conectarse
                try { // después de establecer una conexión y recrear el socket para la comunicación con el cliente, puede ir
                    // para crear flujos de E/S.
                    // ahora podemos recibir mensajes
                    in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                    // y enviar
                    out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

                    String word = in.readLine(); // esperar a que el cliente nos escriba algo
                    System.out.println(word);
                    // sin dudarlo responde al cliente
                    out.write("¡Hola, este es el servidor! Confirmo que escribiste: " + word + "\n");
                    out.flush(); // saca todo del búfer

                } finally { // en cualquier caso, el socket se cerrará
                    clientSocket.close();
                    // flujos también sería bueno para cerrar
                    in.close();
                    out.close();
                }
            } finally {
                System.out.println("¡Servidor cerrado!");
                    server.close();
            }
        } catch (IOException e) {
            System.err.println(e);
        }
    }
"Cliente.java"
import java.io.*;
import java.net.Socket;

public class Client {

    private static Socket clientSocket; // conector para comunicación
    private static BufferedReader reader; // necesitamos un lector que lea desde la consola, de lo contrario
    // ¿sabemos lo que el cliente quiere decir?
    private static BufferedReader in; // flujo de lectura de socket
    private static BufferedWriter out; // flujo de escritura de socket

    public static void main(String[] args) {
        try {
            try {
                // dirección - host local, puerto - 4004, igual que el servidor
                clientSocket = new Socket("localhost", 4004); // con esta línea solicitamos
                // el servidor tiene acceso a la conexión
                reader = new BufferedReader(new InputStreamReader(System.in));
                // leer mensajes del servidor
                in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                // escribe ahi
                out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

                System.out.println("¿Tenías algo que decir? Escríbelo aquí:");
                // si se produjo la conexión y los subprocesos se crearon correctamente, podemos
                // trabajar más y ofrecer al cliente algo para entrar
                // si no, se lanzará una excepción
                String word = reader.readLine(); // esperar a que el cliente haga algo
                // no escribirá en la consola
                out.write(word + "\n"); // enviar un mensaje al servidor
                out.flush();
                String serverWord = in.readLine(); // esperar a que el servidor diga
                System.out.println(serverWord); // recibido - mostrar
            } finally { // en cualquier caso, debe cerrar el socket y los flujos
                System.out.println("El cliente ha sido cerrado...");
                clientSocket.close();
                in.close();
                out.close();
            }
        } catch (IOException e) {
            System.err.println(e);
        }

    }
}
Por supuesto, primero debes iniciar el servidor, porque ¿a qué se conectará el cliente al inicio si no hay algo que lo conecte? :) El resultado será así: /* ¿ Querías decir algo? Introdúcelo aquí: Hola, ¿servidor? ¿Puedes oírme? Hola, este es el servidor! Lo confirmo, escribiste: Hola, servidor? ¿Puedes oírme? El cliente estaba cerrado... */ ¡Hurra! ¡Le enseñamos al servidor a comunicarse con el cliente! Para que la comunicación no se produzca en dos réplicas, sino en tantas como desee, simplemente envuelva la lectura y escritura de los hilos en un bucle while (verdadero) e indique para la salida que, según un determinado mensaje, por ejemplo, “salir” , el ciclo se interrumpía y el programa finalizaría. Cabeza siete: Multiusuario es mejor. El hecho de que el servidor pueda escucharnos es bueno, pero sería mucho mejor si pudiéramos comunicarnos con alguien de nuestro tipo. Adjuntaré todas las fuentes al final del artículo, por lo que aquí mostraré fragmentos de código no siempre grandes, pero sí importantes, que permitirán, si se usan correctamente, crear un chat multiusuario. Entonces, queremos poder comunicarnos con algún otro cliente a través del servidor. ¿Cómo hacerlo? Obviamente, dado que el programa cliente tiene su propio método main, significa que se puede ejecutar por separado del servidor y en paralelo con otros clientes. ¿Qué nos aporta esto? De alguna manera, es necesario que con cada nueva conexión el servidor no se comunique inmediatamente, sino que escriba esta conexión en alguna lista y comience a esperar una nueva conexión, y algún tipo de servicio auxiliar se comunique con un específico. cliente. Y los clientes deben escribir en el servidor y esperar una respuesta independientemente unos de otros. Los hilos vienen al rescate. Digamos que tenemos una clase responsable de recordar nuevas conexiones: debería tener especificado lo siguiente:
  1. Número de puerto.
  2. La lista en la que escribe la nueva conexión.
  3. Y ServerSocket, en una sola (!) copia.
public class Server {

    public static final int PORT = 8080;
    public static LinkedList<ServerSomthing> serverList = new LinkedList<>(); // lista de todos los hilos

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(PORT);
            try {
            while (true) {
                // Bloquea hasta que se realiza una nueva conexión:
                Socket socket = server.accept();
                try {
                    serverList.add(new ServerSomthing(socket)); // agregar una nueva conexión a la lista
                } catch (IOException e) {
                    // Si falla, el socket se cierra,
                    // de lo contrario, el hilo lo cerrará cuando salga:
                    socket.close();
                }
            }
        } finally {
            server.close();
        }
    }
}
Bien, ahora cada socket recreado no se perderá, sino que se almacenará en el servidor. Más. Todo cliente necesita que alguien le escuche. Creemos un hilo con las funciones del servidor del último capítulo.
class ServerSomthing extends Thread {

    private Socket socket; // socket a través del cual el servidor se comunica con el cliente,
    // excepto por eso: el cliente y el servidor no están conectados de ninguna manera
    private BufferedReader in; // flujo de lectura de socket
    private BufferedWriter out; // flujo de escritura de socket

    public ServerSomthing(Socket socket) throws IOException {
        this.socket = socket;
        // если потоку ввода/вывода приведут к генерированию исключения, оно пробросится дальше
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        start(); // llamar a ejecutar()
    }
    @Override
    public void run() {
        String word;
        try {

            while (true) {
                word = in.readLine();
                if(word.equals("stop")) {
                    break;                }
                for (ServerSomthing vr : Server.serverList) {
                    vr.send(word); // enviar el mensaje recibido con
                   // cliente vinculado a todos los demás, incluido él
                }
            }

        } catch (IOException e) {
        }
    }

    private void send(String msg) {
        try {
            out.write(msg + "\n");
            out.flush();
        } catch (IOException ignored) {}
    }
}
Entonces, en el constructor del hilo del servidor, se debe inicializar un socket a través del cual el hilo se comunicará con un cliente específico. También subprocesos de E/S y todo lo demás que necesita para iniciar un subproceso directamente desde el constructor. Bien, pero ¿qué sucede cuando el hilo del servidor lee un mensaje del cliente? ¿Enviar de vuelta sólo a su cliente? No muy efectivo. Estamos creando un chat multiusuario, por lo que necesitamos que cada cliente conectado reciba lo que escribió una persona. Debe utilizar la lista de todos los hilos del servidor asociados con sus clientes y enviar cada mensaje enviado a un hilo específico para que lo envíe a su cliente:
for (ServerSomthing vr : Server.serverList) {
    vr.send(word); //enviar el mensaje recibido
    // del cliente vinculado a todos los demás, incluido él
}
private void send(String msg) {
    try {
        out.write(msg + "\n");
        out.flush();
    } catch (IOException ignored) {}
}
¡Ahora todos los clientes sabrán lo que dijo uno de ellos! Si no desea que el mensaje se envíe a la persona que lo envió (¡ya sabe lo que escribió!), simplemente al iterar por los hilos, especifique que al procesar un objeto, el thisbucle se moverá al siguiente elemento sin realizar cualquier acción al respecto. O, si lo prefiere, envíe un mensaje al cliente indicando que el mensaje fue recibido y enviado exitosamente. Ahora todo está claro con el servidor. ¡Pasemos al cliente, o mejor dicho a los clientes! Allí todo es igual, por analogía con el cliente del último capítulo, solo que al crear una instancia es necesario, como se mostró en este capítulo con el servidor, crear todo lo necesario en el constructor. Pero, ¿qué pasa si al crear un cliente aún no ha tenido tiempo de ingresar nada, pero ya le han enviado algo? (Por ejemplo, el historial de correspondencia de quienes ya se conectaron al chat antes que él). Por lo tanto, los ciclos en los que se procesarán los mensajes enviados deben separarse de aquellos en los que los mensajes se leen desde la consola y se envían al servidor para reenviarlos a otros. Los hilos vuelven al rescate. No tiene sentido crear un cliente como hilo. Es más conveniente crear un hilo con un bucle en el método de ejecución que lea mensajes y también, por analogía, escriba:
// subproceso de lectura de mensajes del servidor
private class ReadMsg extends Thread {
    @Override
    public void run() {

        String str;
        try {
            while (true) {
                str = in.readLine(); // esperando un mensaje del servidor
                if (str.equals("stop")) {

                    break; // salir del bucle si es "stop"
                }
                            }
        } catch (IOException e) {

        }
    }
}
// enhebrar el envío de mensajes provenientes de la consola al servidor
public class WriteMsg extends Thread {

    @Override
    public void run() {
        while (true) {
            String userWord;
            try {
               userWord = inputUser.readLine(); // mensajes de la consola
                if (userWord.equals("stop")) {
                    out.write("stop" + "\n");
                    break; // salir del bucle si es "stop"
                } else {
                    out.write(userWord + "\n"); // enviar al servidor
                }
                out.flush(); // limpiar
            } catch (IOException e) {

            }

        }
    }
}
En el constructor del cliente sólo necesitas iniciar estos hilos. ¿Cómo cerrar adecuadamente los recursos de un cliente si quiere irse? ¿Necesito cerrar los recursos de subprocesos del servidor? Para hacer esto, lo más probable es que necesite crear un método separado que se llame al salir del bucle de mensajes. Allí deberá cerrar el socket y los flujos de E/S. La misma señal de fin de sesión para un cliente en particular debe enviarse a su subproceso de servidor, que debe hacer lo mismo con su socket y eliminarse de la lista de subprocesos en la clase de servidor principal. Cabeza ocho: No hay límite para la perfección. Puedes inventar infinitamente nuevas funciones para mejorar tu proyecto. Pero, ¿qué se debe transferir exactamente a un cliente recién conectado? Creo que los últimos diez acontecimientos ocurrieron antes de su llegada. Para hacer esto, debe crear una clase en la que la última acción con cualquier hilo del servidor se ingresará en la lista declarada, y si la lista ya está llena (es decir, ya hay 10), elimine la primera y agregue el último que vino. Para que el contenido de esta lista sea recibido por una nueva conexión, al crear un hilo del servidor, en el flujo de salida, debe enviarlo al cliente. ¿Cómo hacerlo? Por ejemplo, así:
public void printStory(BufferedWriter writer) {
// ...
}
El hilo del servidor ya ha creado secuencias y puede pasar la secuencia de salida como argumento. A continuación, sólo necesita pasar todo lo que necesita ser transferido al nuevo cliente en un ciclo de búsqueda. Conclusión: esto es solo lo básico y lo más probable es que esta arquitectura de chat no funcione al crear una aplicación real. Este programa fue creado con fines educativos y en base a él mostré cómo hacer que el cliente se comunique con el servidor (y viceversa), cómo hacerlo para varias conexiones y, por supuesto, cómo se organiza esto en los sockets. Las fuentes se reordenan a continuación, y también se adjunta el código fuente del programa que se analiza. Esta es mi primera experiencia escribiendo un artículo) Gracias por su atención :)
  1. Pensando en Java Enterprise, por Bruce Eckel et. Alabama. 2003
  2. Java 8, la guía completa, Herbert Schildt, novena edición, 2017 (Capítulo 22)
  3. Programación de sockets en Java artículo sobre sockets
  4. Zócalo en la documentación oficial.
  5. ServerSocket en la documentación oficial
  6. fuentes en GitHub
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION