Ejemplos java y C/linux

Google
 

Tutoriales

Enlaces

Licencia

Creative Commons License
Esta obra está bajo una licencia de Creative Commons.
Para reconocer la autoría debes poner el enlace http://www.chuidiang.com

Comunicar con socket dos programas java

En este tutorial vamos a conectar un servidor hecho en java con un cliente en java y ver cómo pasar datos de uno a otro. Para una explicación de lo que es un socket, un servidor, un cliente, puedes ver la primera parte del ejemplo de sockets en C. Aunque sea en C, los coceptos son los mismos. Utilizo la versión de J2SE 1.4.1_01.

Al final hay unos fuentes de ejemplo como aplicaciones independientes. No intentes todo esto desde un applet porque tendrás problemas de permisos.

EL SERVIDOR

Los pasos que debemos dar en el servidor son los siguientes:

Crear el socket servidor

Para hacer el servidor en java tenemos la clase ServerSocket. Al instanciarla usaremos el constructor al que se le pasa un número de servicio (de puerto). Como se vio en C, este número de puerto puede ser cualquier entero entre 1 y 65535. Los números de 1 a 1023 están reservados para servicios del sistema (como ftp, mail, www, telnet, etc, etc). Del 1024 en adelante podemos usarlos a nuestro gusto. Lo único es que no puede haber dos servidores atendiendo al mismo puerto/servicio.

ServerSocket socket = new ServerSocket (35557);

Aceptar un cliente

Una vez creado el servidor, le decimos que empiece a atender conexiones de clientes. Para ello llamamos al método accept(). Este método se queda bloqueado hasta que algún cliente se conecta. Nos devuelve un Socket, que es la conexión con dicho cliente.

Socket cliente = socket.accept();

Podemos aceptar simultáneamente varios clientes, pero para atenderlos necesitaremos programación multitarea o algo similar.

Obtener el InputStream y/o OutputStream

Ahora en cliente tenemos la conexión con el cliente (valga la redundancia). Lo único que tenemos que hacer es obtener de él el OuputStream o InputStream con los métodos getOutputStream() o getInputStream(). La clase OutpuStream nos sirve para enviarle datos al cliente. La clase InputStream nos sirve para leer datos del cliente.

InputStream entrada = cliente.getIntputStream();
OutputStream salida = cliente.getOutputStream();

Construirnos un InputStream y/o un OutputStream más adecuados a nuestras necesidades

Los métodos de estas dos clases para leer o escribr datos son un poco feos, ya que únicamente envían bytes. Suele ser habitual construir alguna otra clase de entrada/salida de datos que tenga métodos más adecuados:

Estas nuevas clases tienen métodos más bonitos de usar (writeInt(), writeChar(), etc)

Envio/Lectura de datos normales (enteros, flotantes, strings)

El envío/lectura de datos normales se hace con las clases DataInputStream y DataOutputStream. No tienen ningún truco especial, basta usar el metodo adecuado (writeInt(), writeFloat(), readInt(), etc). Para strings usaremos los métodos writeUTF() y readUTF(), que envían/leen las cadenas en formato UTF.

Envio/Lectura de clases enteras

Para el envio/lectura de clases normales usaremos los métodos readObject() y writeObject() de ObjectInputStream y ObjectOutputStream. Para poder usar estos métodos las clases que enviemos deben implementar la interface Serializable.

Las clases de tipos de java (Integer, Float, String, etc) implementan esa interface, así que se pueden enviar/leer a través de estos métodos.

Si una clase nuestra contiene atributos que sean primitivos de java (int, float, etc) o clases primitivas (Float, Integer, String, etc), basta con hacer que implemente la interface Serializable para poder enviarlas. No hace falta que escribamos ningún método, simplemente poner que se implementa.

// Esta clase se puede enviar/leer por un socket.
class Atributo implements Serializable
{
   int a;
   String b;
}

Si alguno de los atributos no es primitivo de java, basta con que implemente la misma interface Serializable. Si es así, no tendremos ningún problema.

// Esta clase se puede enviar/leer por un socket
class DatoSocket implements Serializable
{
   int c;
   String d;
   Atributo e; // Esta clase es serializable.
}

Si alguna de las clases no es Serializable, tendremos que implementar los métodos privados

private void writeObject(java.io.ObjectOutputStream out) throws IOException {
    // Enviamos los atributos a y b en un orden cualquiera.
    out.writeInt (a);
    out.writeUtf (b);
}

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
    // Leemos los atributos a y b en el mismo orden que los enviamos en writeObject()
    a = in.readInt();
    b = in.readUtf();
}

En el método writeObject() debermos enviar por el ObjectOutputStream todos los atributos de nuestra clase, en el orden que consideremos adecuado. En el método readObject() deberemos ir leyendo del ObjectInputStream todos los atributos de nuestra clase e ir rellenando nuestros atributos. El orden en que leemos debe ser el mismo que en el que escribimos y el formato leido el mismo que el escrito.

En los dos cuadros de ejemplo de arriba hemos puesto el código de ambos métodos para la clase Atributo, pero realmente no hace falta escribir este código. En los fuentes de ejemplo aparece el código, pero está comentado. Si lo descomentas funcionará igual.

Para enviar una de estas clases el código es sencillo

DatoSocket dato = new DatoSocket();
cliente.writeObject(dato);

Para leerlo es igual de simple, sólo que tenemos que saber qué tipo de clase estamos recibiendo para hacer el "cast" adecuado.

DatoSocket dato;
Object aux;
aux = socket.readObject();// Se lee el objeto
if (aux instanceof DatoSocket) // Se comprueba si es de tipo DatoSocket
    dato = (DatoSocket)aux; // Se hace el cast.

Debemos ir haciendo varios if con las posibles clases que nos envíen desde el otro lado.

Cierre del socket

Para cerrar un socket hay que llamar a la función close().

cliente.close(); // Con esto se cierra la conexión con el cliente.
socket.close(); // Con esto se cierra el socket servidor, ya no atendemos más conexiones.

EL CLIENTE

Para el cliente tenemos la clase Socket. Basta instanciarla indicandole contra que máquina conectarse y el puerto con el que debe conectarse. Debe ser el mismo que el puerto que está atendiendo el servidor.

Socket socket = new Socket ("localhost", 35557);

El resto es igual que en el servidor.

EL EJEMPLO

Tienes los fuentes de un ejemplo entre un servidor y un cliente. SocketServidor.java es el servidor, SocketCliente.java es el cliente, Atributo.java es un atributo de una clase a enviar y DatoSocket.java es la clase a enviar, que contiene un Atributo. Puedes bajarte los fuentes, quitarles la extensión .txt y compilarlos y ejecutarlos. Si todo está en el mismo directorio, puedes compilarlos desde una shell de linux o una ventana de ms-dos con

$ javac SocketServidor.java SocketCliente.java DatoSocket.java Atributo.java

Para ejecutarlo, abre dos shell o ventanas de ms-dos distintas, situadas en el directorio donde esté todo esto y escribe

$ java SocketServidor
$ java SocketCliente

Estadísticas y comentarios

Numero de visitas desde el 4 Feb 2007: