- Un ejemplo de un cliente / servidor de ECHO

 

El servicio ECHO suele estar instalado en el puerto 7 de máquinas Unix y permite comprobar que la máquina está operativa y que se puede establecer una conexión con dicha máquina.

El funcionamiento de un servidor ECHO es muy sencillo, cuando alguien se conecta espera que le envíe algo y le responde exactamente con la misma información recibida. El cliente actúa de forma contraria; envía datos al servidor y luego comprueba que los datos recibidos son idénticos a los transmitidos.

Ejercicio: Estudio del protocolo ECHO con el comando telnet.

El siguiente ejercicio nos muestra un truco para testear si un servidor que utiliza TCP funciona correctamente. Te recomendamos que lo utilices antes de realizar la programación del cliente. Por una parte, te permitirá asegurarte que tanto la conexión, como el servidor funcionan correctamente. Por otra parte, te asegurarás que has entendido correctamente el protocolo a implementar.

NOTA: Para que el ejemplo funcione en la dirección IP 158.42.146.127 ha de haber un servidor de ECHO en funcionamiento. En caso de no ser así, puedes reemplazar la IP por la de un servidor de ECHO en funcionamiento. También puedes implementar tu propio servidor de ECHO,  tal y como se muestra en uno de los siguientes ejercicios.

1.     Para verificar si el servidor de ECHO está en marcha,  desde un intérprete de comandos (símbolo del sistema/shell) escribe:

  telnet 158.42.146.127 7

Este comando permite establecer una conexión TCP con el puerto 7 del servidor. A partir de ahora todo lo que escribas se enviará al servidor (aunque en muchos clientes Telnet no se muestra en pantalla lo que escribes) y todo lo que el servidor envíe se imprimirá en pantalla.

 

NOTA: Windows 7 tiene desactivado por defecto el comando Telnet. Para habilitarlo, haz clic en el menú Inicio > Panel de control > Programas > Activar o desactiva las características de Windows y marca Cliente Telnet.

 

2.     Espera a que se establezca la conexión. De no ser posible, vuelve a intentarlo o lee la nota del principio del ejercicio.

3.     Escribe una frase cualquiera y pulsa <Intro>. Por ejemplo:

Hola hola

Si te equivocas no uses la tecla de borrar. En tal caso, repite el ejercicio desde el punto 3. Equivocarse en este protocolo no tiene ninguna repercusión, pero en el resto que vamos a estudiar hará que el servidor no nos entienda.

4.     Observa que la respuesta obtenida coincide con la frase que has introducido. Además el servidor cerrará inmediatamente la conexión y no te permitirá mandar más frases.

Ejercicio: Un cliente de ECHO

El siguiente ejemplo muestra cómo podrías desarrollar un cliente de ECHO que utiliza unsocket stream desde Android.

NOTA: Para que el ejemplo funcione en la dirección IP 158.42.146.127 ha de haber un servidor de ECHO en funcionamiento. En caso de no ser así, puedes reemplazar la IP por la de un servidor de ECHO en funcionamiento o realizar el siguiente ejercicio.

1.     Crea una nueva aplicación con los siguientes datos:

Application Name: Cliente ECHO

     Package Name: org.example.clienteecho

☑Phone and Tablet

Minimum SDK: API 9 Android 2.3 (Gingerbread)

Add an activity: Empty Activity

 

NOTA: Utilizamos como versión mínima la 2.3 para poder configurar StrictMode (se explica a continuación).

2.     Añade la etiqueta android:id="@+id/TextView01" en el TextView del layout de la actividad.

3.     Reemplaza el código de la actividad por:

public class MainActivity extends Activity {
   private TextView output;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      output = (TextView) findViewById(R.id.TextView01);
      StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .permitNetwork().build());
      ejecutaCliente();
   }

   private void ejecutaCliente() {
      String ip = "158.42.146.127";
      int puerto = 7;
      log(" socket " + ip + " " + puerto);
      try {
         Socket sk = new Socket(ip, puerto);
         BufferedReader entrada = new BufferedReader(
                  new InputStreamReader(sk.getInputStream()));
         PrintWriter salida = new PrintWriter(
                  new OutputStreamWriter(sk.getOutputStream()), true);
         log("enviando... Hola Mundo ");
         salida.println("Hola Mundo");
         log("recibiendo ... " + entrada.readLine());
         sk.close();
      } catch (Exception e) {
         log("error: " + e.toString());
      }
   }

   private void log(String string) {
      output.append(string + "\n");
   }
}


Las tres primeras líneas del método onCreate() han de resultarte familiares. En la cuarta se configura StrictMode[1]. Consiste en una herramienta de desarrollo que detecta cosas que podrías estar haciendo mal y te llama la atención para que las corrijas. Esta herramienta aparece en el nivel de API 9. Una de las verificaciones que realiza es que no se acceda a la red desde el hilo principal. Este problema se podría resolver lanzando un nuevo hilo para realizar el acceso al servidor[2]. Más adelante se muestra como resolverlo usando AsyncTask. En este apartado queremos centrarnos en el uso de sockets y no en el manejo de hilos, por lo que vamos a desactivar esta comprobación. En la línea en cuestión se indica a StrictMode que en su política dethreads no tenga en cuenta los accesos a la red.

La parte interesante se encuentra en el método ejecutarCliente(). En primer lugar, todo cliente ha de conocer la dirección del socket del servidor; en este caso los valores se indican en el par de variables ip y puerto. Nunca tenemos la certeza de que el servidor admita la conexión, por lo que es obligatorio utilizar una sección try / catch. La conexión propiamente dicha se realiza con el constructor de la clase Socket. Siempre hay que tener previsto que ocurra algún problema, en tal caso, se creará la excepción y se pasará a la sección catch. En caso contrario, continuaremos obteniendo el InputStream y el OutputStream asociado alsocket. Lo cual nos permitirá obtener las variables, entrada y salida mediante las que podremos recibir y transmitir información. El programa transmite la cadena "Hola Mundo", tras lo que visualiza la información recibida. Si todo es correcto, ha de coincidir con lo transmitido.

 

4.     Solicita en la aplicación el permiso INTERNET en AndroidManifest.

<uses-permission android:name="android.permission.INTERNET"/>

5.     Ejecuta la aplicación y verifica si el servidor responde. Recuerda que es posible que no se haya arrancado este servicio en el servidor.

6.   Comenta la línea de onCreate() donde se se configura StrictMode y ejecuta de nuevo la aplicación. El resultado no será satisfactorio. En este caso ocurrirá una excepción NetworkOnMainThreadException.

 

Ejercicio: Un servidor de ECHO

Vamos a implementar un servidor de ECHO en tu ordenador personal. Has de tener claro que no va a ser una aplicación Android, si no un programa 100% Java. 

 

1.     Crea un nuevo proyecto Java y llámalo ServidorECHO.

Crea un nuevo proyecto (File > New Project..). Utiliza la opción Add No Activity, así no creamos una actividad que nunca será usada. Pulsa en File > New Module. Selecciona Java Library y pulsa Next. Introduce en Library name: servidor, como Java pakage name: com.example.servidorecho y en Java class name: ServidorECHO. Pulsa el botón Finish. Se creará un nuevo módulo Java dentro de tu proyecto Android. Pulsa en el botón desplegable a la derecha del botón Run . Selecciona Edit Configurations... En la nueva ventana, haz clic en el signo + de la esquina superior izquierda y selecciona Application. Aparecerá una nueva configuración de aplicación. Selecciona en Name: servidor, en Main class: com.example.servidorecho.ServidorECHO y en Use classpath of module: servidor. Pulsa en OK.

2.     Reemplaza el código de ServidorECHO con el siguiente código:

public class ServidorECHO {
   public static void main(String args[]) {
      try {
         System.out.println("Servidor en marcha...");
         ServerSocket sk = new ServerSocket(7);
         while (true) {
            Socket cliente = sk.accept();
            BufferedReader entrada = new BufferedReader(
                  new InputStreamReader(cliente.getInputStream()));
            PrintWriter salida = new PrintWriter(new OutputStreamWriter(
                  cliente.getOutputStream()), true);
            String datos = entrada.readLine();
            salida.println(datos);
            cliente.close();
            System.out.println(datos);
         }
      } catch (IOException e) {
         System.out.println(e);
      }
   }
}

En este caso utilizaremos la clase ServerSocket asociado al puerto 7 para crear un socket que acepta conexiones. Luego, se introduce un bucle infinito para que el servidor esté perpetuamente en servicio. El método accept() bloquea al servidor hasta que un cliente se conecte. Cuando ocurra esto, todo el intercambio de información se realizará a través de un nuevo socket creado con este propósito, el socket cliente. El resto del código es similar al cliente, aunque en este caso primero se recibe y luego se transmite lo mismo que ha recibido.

 

3.     Sustituye la dirección IP en ClienteECHO por la IP de tu ordenador. El comando ipconfig (Windows) o ifconfig (Linux/Mac) te permite averiguar la dirección IP de tu ordenador. No utilices como IP 127.0.0.1 (localhost) dado que, aunque se ejecuten en la misma máquina, la IP del emulador es diferente a la del PC.

4.     Ejecuta primero el servidor y luego el cliente para verificar que funciona.

 

NOTA: Si la IP de tu ordenador es privada, no podrás crear un servidor accesible desde cualquier parte de Internet. En este caso utiliza para el cliente un emulador o un dispositivo Android real que se conecte por Wi-Fia la misma red de tu ordenador. De lo contrario, el cliente no encontrará el servidor.

 

[1] http://developer.android.com/reference/android/os/StrictMode.html

[2]En el capítulo 5 se describe este problema con más detalle