Mostrando entradas con la etiqueta HTML. Mostrar todas las entradas
Mostrando entradas con la etiqueta HTML. Mostrar todas las entradas

AndroidWebServer

La aplicación

Para construir un servidor web es necesario contar con un programa que haga las veces de “servidor”, es decir, que esté escuchando siempre en uno o varios sockets a la espera de algún cliente para servirle la información solicitada.
La interfaz gráfica será muy sencilla, una etiqueta para mostrar la IP del servidor, un botón para encender o apagar el servidor web y una consola para ver que es lo que está sucediendo, es decir llevar un registro de eventos.

Antes de continuar: Este trabajo está también disponible en versión PDF, lo cual considero, es más cómodo de leer. Lo puedes descargar desde aquí.

Interfaz gráfica

Como se menciona anteriormente, la interfaz es bastante simple, pensando en que de inicio el servidor no es una aplicación que tenga que ser manipulada constantemente por el usuario final.
La interfaz queda abierta a ampliaciones, según se vaya dotando de funcionalidad a esta aplicación.

ServerSocket

Sobre la plataforma Android, el manejo de los sockets es similar al que se realiza en cualquier otra aplicación de escritorio.
Así que simplemente abrimos un ServerSocket
//Fragmento de código del archivo WebServerActivity.java
ServerSocket ss;
try {
    ss = new ServerSocket(puerto);
    while (!isCancelled()) {
        Socket entrante = ss.accept();
//...

Permisos

Para hacer uso de los sockets es necesario otorgar permiso de INTERNET en el manifiesto de la aplicación, lo cual se consigue añadiendo la línea
<uses-permission android:name="android.permission.INTERNET" />
Ya que de otro modo  obtendremos una excepción de IO.

Complicaciones con el manejo de los sockets

En Android no es posible trabajar con sockets en el hilo principal, por tanto no es posible utilizar el código anteriormente mostrado dentro de cualquier método. Por ejemplo, no podemos colocarlo en el método onCreate, ni colocarlo directamente dentro de algún manejador de eventos ya que al tratar de ejecutarlo nos lanzará la excepción:
android.os.NetworkOnMainThreadException
Por tanto es necesario utilizar una clase interna que extienda de AsyncTask para trabajar con el ServerSocket
//Fragmento de código del archivo WebServerActivity.java
public class ServidorWeb
    extends AsyncTask<WebServerActivity, String, Void> {

En cuyo método sobreescrito doInBackground, añadiremos el código necesario para inicializar el socket:
//Fragmento de código del archivo WebServerActivity.java
protected Void doInBackground(WebServerActivity... arg0) {
    try {
        ss = new ServerSocket(puerto);
        while (!isCancelled()) {
            Socket entrante = ss.accept();

Para comenzar la ejecución basta con crear un objeto de nuestra clase nueva, ServidorWeb y llamar al método execute, por la manera en la que se trabajó, al método es necesario pasarle la referencia a la clase de nuestra actividad.
//Fragmento de código del archivo WebServerActivity.java
ServidorWeb servidorWeb;
servidorWeb = new ServidorWeb();
servidorWeb.execute(this);

Registro de eventos

En la interfaz gráfica incluimos un control (View) para llevar el registro de eventos, un símil de consola, pero sin serlo necesariamente, ya que no admite parámetros de entrada ni escritura sobre ella.
Para añadir texto bastaría con llamar al método setText, sin embargo es aquí en donde existe otra limitante de Android, y es que los controles solo pueden ser manipulados directamente por el hilo que los creó, todos los controles son creados por el hilo de UI, y no se pueden modificar por otro sino ese. Recordaremos que los eventos son lanzados desde el servidor, que está en otra clase, y peor aún, corre en otro hilo.
Hacer una llamada como: ServerConsole.append(Html.fromHtml("Buen día señor sol")); desde cualquier otra clase que no sea la de la actividad en cuestión resultará en una excepción:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Para nuestra suerte, existe una solución y es haciendo uso del método onProgressUpdate,con el cual podemos modificar cosas que estén corriendo en el hilo de UI
//Fragmento de código del archivo WebServerActivity.java
@Override
protected void onProgressUpdate(String... values) {
      /* logView es un método implementado en la clase WebServerActivity, en donde fueron creados los controles de la interfas. Se encarga de añadir el texto al control ServerConsole */
      logView((values[0]), level);
}
Contrario a lo que pareciera uno no debe llamar directamente a este método. Si no hacer uso del método (también proporcionado por la clase AsyncTask) publishProgress el cual, a cada llamada, provocará que el método onProgressUpdate se ejecute.
Esa es la manera en la que estaremos modificando la interfaz desde el hilo en el que correrá nuestro servidor.

Apagando y encendiendo el servidor

Para terminar la ejecución del código existente dentro de una AsyncTask tenemos a nuestra disposición el método cancel, del cual nos vamos a valer para apagar el servidor si es que no queremos que siga procesando solicitudes, dada la arquitectura de nuestro servidor, también es necesario cerrar el socket, ya que al estar esperando un cliente, no se apaga de inmediato.
//Fragmento de código del archivo WebServerActivity.java
servidorWeb.cancel(true);
servidorWeb.closeSocket();

Aceptando, leyendo y respondiendo la petición

Una vez que todo el esqueleto esté armado, es necesario adentrarnos a un nivel inferior, al núcleo del servidor, la forma en la que se hace una petición web es aceptando la conexión con un socket creado internamente, a través del cual podemos leer y escribir información al cliente que lo solicite.
Para realizar esto tenemos el siguiente código dentro de doInBackground:
//Fragmento de código del archivo WebServerActivity.java
///Mientras la tarea no haya sido cancelada
while (!isCancelled()) {
      ///Quedamos a la espera de un cliente nuevo
      Socket entrante = ss.accept();
///Actualizamos la “consola” con la información del cliente
///aceptado
      publishProgress("Cliente aceptado: <b>"                                     + entrante.getInetAddress().getHostName() + "</b>",
            "1");
///Usaremos la clase auxiliar PeticionWeb para procesar
      PeticionWeb pw = new PeticionWeb(entrante, this);
///Dado que PeticionWeb extiende de Thread, lo iniciamos
      pw.start();
}

La clase PeticionWeb


//Fragmento de código del archivo PeticionWeb.java
public class PeticionWeb extends Thread {
      private ServidorWeb web;
      privateSocket scliente = null;
Esta clase será el auxiliar encargado de procesar la petición, extiende de Threadpara que corra de manera que no bloquee a las demás peticiones entrantes. En ella se obtienen los flujos de entrada y de salida, que son los canales de comunicación con el cliente que está interesado en consumir de nuestro servidor.
El siguiente código es la implementación sobreescrita de run adaptada a nuestras necesidades.

//Fragmento de código del archivo PeticionWeb.java
@Override
public void run() {
      try {
            /* Obtenemos los flujos de entrada y salida, así como un objeto instancia de PrintWriter para escribir en el cliente */
            BufferedReader in = new BufferedReader(newInputStreamReader(
                        scliente.getInputStream()));
            OutputStream outputStream = scliente.getOutputStream();
            PrintWriter out = new PrintWriter(newOutputStreamWriter(
                        outputStream), true);
            out.flush();

            String cadena = "";
            /* ProcesaPeticion es la clase que usaremos para recuperar el contenido de los archivos almacenados en las carpetas del servidor */
            ProcesaPeticion pp = null;
            /* Leemos del flujo de entrada, mientras la lectura no sea nula continuaremos */
            while ((cadena = in.readLine()) != null) {

                  if (cadena != null ///es
&& !"".equals(cadena)) {
/* La lectura de una línea en blanco marca el final de la petición HTTP */
                        if (cadena.startsWith("GET")) {
                        /* Revisamos el contenido de la cadena que comience con GET, ya que es ahí en donde viene la información del archivo que se desea consultar */
                             cadena = cadena.substring(4);
                  cadena = cadena.substring(0,  
cadena.indexOf(" "));
                             web.log("Petición: " + cadena);
/* Dejamos que la clase auxiliar trate de buscar el archivo y devolver una respuesta */
                             pp = newProcesaPeticion(cadena);
                        }
                  } else
                        break;
            }
/* Escribimos las cabeceras de la respuesta y el contenido solicitado, el cual nos lo devolverá el método getResponse */
            out.println("HTTP/1.0 200 OK");
            out.println("Server: " + web.getIpAddr());
            out.println("Date: " + new Date());
            out.println("Content-Type: text/html");
            out.println("");
            if (pp != null) {
                  out.println(pp.getResponse());
            }
            out.flush();
            out.close();
      } catch (Exception e) {
            web.log("Error en servidor: " + e.toString(), "2");
      }
}

La clase ProcesaPeticion

Esta clase es el puente entre los archivos físicos en el servidor y la respuesta que el cliente espera recibir del servidor, no olvidar que, como estamos guardando los archivos del servidor en la memoria externa del dispositivo necesitamos añadir otro permiso al manifiesto de la app.
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
El código:
public class ProcesaPeticion {
      String path;
      String response;

      public ProcesaPeticion(String path) throwsException {
            this.path = path;
            // Checamos si está montada la SD:
            String state = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(state)) {
                  // Variables File que nos ayudarán más adelante
                  File sdCard = Environment.getExternalStorageDirectory()
,peticion
,contenedor;
                  // Los documentos deberán existir en la carpeta
                  // "AndroidWebServer/wwwroot"
                  contenedor = new File(sdCard.getAbsolutePath()
                             + "/AndroidWebServer/wwwroot");
                  contenedor.mkdirs();
                  File request = new File(contenedor.getAbsolutePath() + path);
                  StringBuilder strLine = new StringBuilder();
                  if(request.exists() && request.isFile()) {
                        try {
                             // Lectura del archivo
                        } catch (Exception e) {
                        }
                  } else if(request.exists() && request.isDirectory()){
                        // Aquí manejaremos cuando la solicitud sea a un // directorio
                  } else {
                        // La ruta que solicitó no existe
                  }
                  response = strLine.toString();
            } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            } else {
                  throw new Exception("No hay una memoria SD en el dispositivo");
            }
      }
     
}



Referencias

Wikipedia
Documentación de Android Developers



Contacto

Si tienes alguna duda con respecto a la aplicación puedes dejar un comentario en la siguiente liga
Enviarme un correo a antonio.feregrino@gmail.com
Mandarme un tweet a @fferegrino o @IO_Exception
No olvides que la aplicación está disponible en Google Play, la tienda de aplicaciones para Android en la siguiente liga:


¡Saludos!
@fferegrino :)

De C a HTML con Flex (como Norman manda)

Ya hace unos días les había hablado de flex y la práctica que teníamos que hacer. Bien, resulta que apenas se entrega. Y no es como inicialmente la había hecho:

Lo que había hecho fue de manera individual y esta que subo hoy es en equipo (Mi equipo: Byron, Gaby y Robert).
Las funciones que escriben el archivo html están por separado. Para mejor explicación visita el post anterior.

Descargas
PracticaC2HTML.tar.gz
PracticaC2HTML.zip


¡Saludos!
@fferegrino :)

De C a HTML con Flex

De nuevo por acá, esta vez gracias a otra de mis asignaturas en la ESCOM. Toca el turno a Teoría computacional, fue el turno de usar Flex, herramienta que en lo particular no conocía. Lo que teníamos que hacer era usar Flex para escribir un programa que de un archivo con código en lenguaje C nos lo "transfiriera" a un archivo con formato html estilizado con CSS.

La idea era encontrar palabras reservadas, tipos de datos, identificadores, directivas, operadores, y cualquier otro tipo de cadenas permitidas con el analizador léxico y asignarles cierto estilo en un documento html. 

Trato de explicar gráficamente: la imagen de la izquierda es código en C, después de introducir este archivo en el programita nos debe entregar algo como la captura derecha, que es un na representación estilizada con CSS del archivo que introdujimos.



¿Qué es Flex?
Resulta que Flex, en términos prácticos (Wikipediazo), es un programa para generar analizadores léxicos. Uno mete la especificación de un analizador léxico y Flex regresa como salida la implementación en C de lo que nosotros introdujimos. En realidad es muy sencillo utilizarlo, solo hay que tener en cuenta la estructura del archivo que vamos a usar como entrada al programa, esta es sencilla y muy fácil de recordar, un archivo "lex", se divida en tres secciones (separadas por líneas que solo contienen '%%' como texto en ellas). Las líneas son:
1.- Sección de declaraciones
Lugar para incluir cabeceras, macros, variables globales... también es posible definir patrones a usar en la sección de reglas
%%
2.- Sección de reglas
Aquí es donde se asocian los patrones(expresiones regulares) a buscar con sentencias(código) de C. Cada vez que el analizador encuentra un a cadena que coincide con el patrón, se ejecuta el código en C
%%
3.- Sección de código en C
Contiene sentencias en C y funciones que serán copiadas íntegramente en el archivo que Felx genere, comúnmente se colocan aquí las funciones que se llamarán en la sección de reglas.

Descargas
Al compilar el programa en Flex nos devuelve un código compilable pero ahora en lenguaje C, el cual ya debería de hacer lo que nosotros le indicamos. En el descargable básico viene el archivo cToHtml.l que es el archivo en lenguaje Lex, el archivo de estilos estilos.css que es la hoja de estilos que se usa para formatear el documento de salida y el archivo Makefile que nos facilita la compilación y el archivo Leeme.txt PARA QUE LO LEAN, en donde viene como se usa el programa final. En el descargable completo, viene, además de lo indicado arriba, el archivo lex.yy.c que es el código que se genera al compilar en Flex y el archivo salida.html que es un ejemplo de lo que entrega nuestro programa si hacemos pasar por el a lex.yy.c.

Recuerda que para cualquier duda, comentario o invitación a una cerveza, está la sección de comentarios, mi cuenta en Twitter o mi correo. 

¡Saludos! @fferegrino :)

Librero



Librero, un proyecto en el que estoy trabajando del cual me gustaría tener noticias pronto. Lástima que este semestre en ESCOM no haya tomado muchas materias en las cuales desarrollarlo.

Me gustaría llevármelo hasta TT (algo como tesis).



¡Saludos!
@fferegrino :)