Saludos
Hoy me gustaría analizar el proceso de escribir aplicaciones cliente-servidor que realizan las funciones de las utilidades estándar de Windows, como Telnet, TFTP, etc., etc. en Java puro. Está claro que no traeré nada nuevo: todas estas utilidades han funcionado con éxito durante más de un año, pero creo que no todos saben lo que sucede debajo del capó.
Esto es lo que se discutirá debajo del corte.
En este artículo, para no retrasarlo, además de la información general, escribiré solo sobre el servidor Telnet, pero en este momento todavía hay material sobre otras utilidades, estará en las partes posteriores del ciclo.
En primer lugar, debe comprender qué es Telnet, por qué es necesario y con qué se come. No citaré las fuentes textualmente (si es necesario; al final del artículo adjuntaré un enlace a los materiales relacionados), solo puedo decir que Telnet proporciona acceso remoto a la línea de comando del dispositivo. En general, aquí es donde termina su funcionalidad (silenciosamente contacté silenciosamente el puerto del servidor conscientemente, más sobre eso más adelante). Entonces, para su implementación, debemos aceptar la línea en el cliente, transferirla al servidor, intentar transferirla a la línea de comando, leer la respuesta de la línea de comando, si la hay, transferirla nuevamente al cliente y mostrarla, o, en caso de que ocurra errores, haga que el usuario entienda que algo está mal.
Para implementar lo anterior, en consecuencia, necesita 2 clases de trabajo y alguna clase de prueba desde la cual iniciaremos el servidor y a través del cual trabajará el cliente.
En consecuencia, en este momento, la estructura de la aplicación incluye:
- Telnetclient
- TelnetClientTester
- TelnetServer
- TelnetServerTester
Repasemos cada uno de ellos:
TelnetclientTodo lo que esta clase debería poder hacer es enviar los comandos recibidos y mostrar las respuestas recibidas. Además, debe poder conectarse a un puerto arbitrario (como se mencionó anteriormente) del dispositivo remoto y desconectarse de él.
Para esto, se implementaron las siguientes funciones:
Una función que toma una dirección de socket como argumento, abre una conexión e inicia flujos de entrada y salida (las variables de flujo se declaran arriba, fuentes completas al final del artículo).
public void run(String ip, int port) { try { Socket socket = new Socket(ip, port); InputStream sin = socket.getInputStream(); OutputStream sout = socket.getOutputStream(); Scanner keyboard = new Scanner(System.in); reader = new Thread(()->read(keyboard, sout)); writer = new Thread(()->write(sin)); reader.start(); writer.start(); } catch (Exception e) { System.out.println(e.getMessage()); } }
Sobrecargando la misma función, conectándose al puerto predeterminado - para telnet esto es 23
public void run(String ip) { run(ip, 23); }
La función lee los caracteres del teclado y los envía al zócalo de salida, lo cual es típico, en la cadena en lugar del modo de caracteres:
private void read(Scanner keyboard, OutputStream sout) { try { String input = new String(); while (true) { input = keyboard.nextLine(); for (char i : (input + " \n").toCharArray()) sout.write(i); } } catch (Exception e) { System.out.println(e.getMessage()); } }
La función recibe datos del socket y los muestra en la pantalla.
private void write(InputStream sin) { try { int tmp; while (true){ tmp = sin.read(); System.out.print((char)tmp); } } catch (Exception e) { System.out.println(e.getMessage()); } }
La función deja de recibir y transmitir datos.
public void stop() { reader.stop(); writer.stop(); } }
TelnetServerEsta clase debe tener la funcionalidad de aceptar un comando de un socket, enviarlo para su ejecución y enviar una respuesta del comando al socket. El programa no verifica intencionalmente los datos de entrada, porque en primer lugar, existe la oportunidad de formatear el disco del servidor en la "caja telnet", y en segundo lugar, el problema de seguridad en este artículo se omite en principio, y es por eso que no hay una palabra sobre cifrado o SSL
Solo hay 2 funciones (una de ellas está sobrecargada), y en general esto no es una buena práctica, sin embargo, como parte de esta tarea, me pareció apropiado dejar todo como está.
boolean isRunning = true; public void run(int port) { (new Thread(()->{ try { ServerSocket ss = new ServerSocket(port);
El programa abre el puerto del servidor, lee los datos del mismo, hasta que encuentra un carácter de terminación de comando, transfiere el comando a un nuevo proceso y la salida del proceso se redirige al socket. Todo es como un rifle de asalto Kalashnikov.
En consecuencia, para esta función hay una sobrecarga con el puerto predeterminado:
public void run() { run(23); }
Bueno, y en consecuencia, la función que detiene el servidor también es trivial, interrumpe el ciclo eterno, violando su condición.
public void stop() { System.out.println("Server was stopped"); this.isRunning = false; }
No daré clases de prueba aquí, están en la parte inferior, todo lo que hacen es verificar el rendimiento de los métodos públicos. Todo está en el gita.
Resumiendo, en un par de noches puede comprender los principios de funcionamiento de las principales utilidades de la consola. Ahora, cuando llegamos a la computadora remota, entendemos lo que está sucediendo: la magia ha desaparecido)
Entonces los enlaces son:
Todas las fuentes estaban, están y estarán aquí.Sobre TelnetMás acerca de Telnet