Comparativa I: Cliente en Ruby

Pasamos de Java a Ruby, un lenguaje que a pesar de ser aproximadamente tan viejo como el primero, aún sigue siendo desconocido por algunos europeos. Hace poco ha tenido un auge en el viejo continente gracias, en gran medida, a RubyOnRails.

Puesto que vamos a necesitar realizar conexiones, lo primero es:

require 'socket'

Acto seguido, se define el método mensaje_aleatorio:

def mensaje_aleatorio
msg = ["Hola, Mundo","Melón.","Larga y próspera vida.","La Quinta Raza.","So long, and thanks for all the fish.","Spain is different.", "Com va això?","Bona tarda."]
msg[rand(msg.length)]
end

Y ahora llega la magia Ruby. Para crear diez hilos diferentes simplemente se utiliza el método times y la clase Thread:

num_hilos = 10
num_hilos.times do |i|
Thread.start do

En él, se crea un bucle infinito en el que se conecta al servidor (simplemente con TCPSocket.new(servidor,puerto) ), envía el mensaje y espera la respuesta, comprobando que es el texto enviado.

while(true)
puts "Abriendo socket #{j}"
begin
socket = TCPSocket.new("localhost",7777)
puts "Abierto socket #{j}"
s = mensaje_aleatorio
socket.puts s
puts "Enviado \"#{s}\" en #{j}"
puts (s == (s1=socket.gets().strip())) ? "Correcto" : "Falló\nEsperado: #{s}\nDevuelto: #{s1}"
rescue Exception => bang
puts bang.to_s
ensure
socket.close unless socket.nil?
end
end

Ya, por último y fuera del método times, se crea un bucle infinito para evitar que termine el script sin hacer lo que se le pide.

Aquí el código completo.

Comparativa I: Servidor en Java

Segunda entrega de esta comparativa. Le toca el turno al servidor echo. Este servidor constará de dos clases: EchoServer, la clase pública con el método main, y EchoConnection, que extiende la clase Thread y es la que realiza la mayor parte del trabajo.

EchoServer.

Aquí nos encontramos el método main(), el cual es muy, pero que muy simple:

public static final void main(String[] args){
new EchoServer();

}

El constructor de la clase es un poco más interesante, con él se explicará el funcionamiento básico de la clase ServerSocket del paquete java.net.

int port = 7777;
try{
ServerSocket server = new ServerSocket(port);
System.out.println("Servidor a la escucha en el puerto: "+port);

El constructor de la clase ServerSocket recibe como parámetro el número del puerto en el que ha de escuchar. En el siguiente trozo de código se crea un bucle infinito y se lanzan hilos que manejen las peticiones recibidas. Para ello se utiliza el método accept() de la clase ServerSocket. Este método espera una conexión y devuelve el objeto Socket que la representa. Finalmente se capturan las excepciones.
while(true) new EchoConnection(server.accept());
}catch(Exception e){
System.out.println("Error en el servidor.\n"+e.toString());
}

EchoConnection.

La clase EchoConnection extiende la clase Thread y contiene dos variables estáticas: connections y visits. Estas variables sirven para contabilizar el número de clientes totales y el de clientes conectados actualmente. Por otra parte, se declara una variable privada socket, en la que se almacena la conexión pasada al constructor. Dicho constructor, consta de la asignación del socket recibido al privado y de la sentencia que inicia el hilo (this.start()).

El último método a comentar es el método run(). Dicho método es rápido de explicar. En primer lugar, se aumenta el número de conectados y el de las visitas:

EchoConnection.connections++;
int numCliente = ++EchoConnection.visits;

Después se capturan los flujos de entrada y de salida:

BufferedReader entrada = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
PrintWriter salida = new PrintWriter(new OutputStreamWriter(this.socket.getOutputStream()),true);

Ahora se ha de realizar lo que se supone que debe hacer. Recibir una cadena…

String cadena = entrada.readLine();

…y reenviarla:

salida.println(cadena);

Para terminar, se cierran los flujos y el socket:

entrada.close();
salida.close();
this.socket.close();

Después se capturan las excepciones y en el finally se resta 1 al número de conectados.

El código completo se puede encontrar aquí.

Nota: el código no es todo lo limpio que debería ser. Todos los cierres deberían ir en el finally, por ejemplo.

Comparativa I: Cliente en Java.

En primer lugar, decir que no es un cliente “echo”, simplemente es un programita que nos pruebe que el servidor, lo que realmente interesa.

El cliente en sí, es sencillo a más no poder: crea una cantidad de hilos cada uno de los cuales pilla un mensaje aleatorio, lo envía y comprueba que lo recibido es igual al enviado. Esto será así para el resto de lenguajes, por lo que no lo volveré a escribir (que me canso).

Empecemos a ver código. Una vista previa del código fuente (se puede descargar aquí) nos desvelará que consta de tres clases (sólo una de ellas es pública, por lo que sólo necesitaremos un archivo): EchoClient, EchoClientThread y Mensaje. Comentaremos primero las dos primeras, por ser las más cortas y después la segunda, por ser la que realiza todo el trabajo.

EchoClient.

La clase EchoClient es pública y consta de una variable estática (el límite de hilos), limite, y el método main. Dicho método sólo consta de una línea de código, la cual lanza tantos hilos como lo indique la variable estática:

for(int i=0; i < EchoClient.limite; i++) new EchoClientThread();

Mensaje.

La clase Mensaje sólo contiene un método estático:

public static String aleatorio(){
String[] mensajes = {"Hola, Mundo.","Melón.","Larga y próspera vida.","La Quinta Raza.","So long, and thanks for all the fish.","Spain is different.", "Com va això?","Bona tarda."};
return mensajes[ (int) Math.round(Math.floor(Math.random() * mensajes.length)) ];
}

En la primera línea se crea un array de String con diferentes mensajes. En el segundo se genera un número aleatorio y se coge ese elemento del array.

EchoClientThread.

Esta clase extiende de Thread. Su constructor simplemente llama al constructor padre e inicia el hilo. El método run() simplemente tiene un bucle infinito que llama una y otra vez el método conectar(). Este método es el interesante, aparte de largo, por lo que vayamos por partes:

int port = 7777;
String server = "localhost";

Declaramos dos variables, la primera es el puerto y la segunda el servidor al que nos conectaremos. Ahora entramos en un bloque try:

Socket s = new Socket(server,port);
BufferedReader entrada = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter salida = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);

Aquí creamos el socket al servidor y puerto indicados anteriormente. Después se capturan los flujos de entrada y de salida.

String mensaje = Mensaje.aleatorio();
salida.println(mensaje);

Creamos un mensaje aleatorio y se envía al servidor.

String cadena = entrada.readLine();
System.out.println(cadena.equals(mensaje));

Se recoge la respuesta del servidor y se imprime true si es lo enviado o false si es distinto. Por último se cierra la conexión:

s.close();

Y se capturan los errores. Técnicamente el último close() tendría que ir en un finally, etc. pero como ejemplo va bien. Ahora ya sólo se necesita compilarlo. Próximamente, el servidor.

Comparativa I: Introducción

La primera comparativa consistirá en la creación de un servidor y un cliente, ambos multihilo, del protocolo echo en tres lenguajes de programación: Java, Ruby y C# (utilizando el Proyecto Mono).

Introducción: el protocolo echo.

El protocolo echo es muy (y cuando digo muy, me refiero a muy) sencillo: simplemente devuelve todo los datos enviados al servidor sin modificación alguna. Fue utilizado para probar redes IP, siendo sustituido por ping y traceroute.

El puerto, tanto TCP como UDP es el número 7.

Fuente: Wikipedia.