Unidad 10. Internet: sockets, HTTP y servicios web
TutorialesAndroid
Introducción
Los teléfonos Android suelen disponer de conexión a Internet. Esto nos permite no solo almacenar los datos en nuestro dispositivo, si no compartirlos con otros usuarios. En el primer punto del capítulo trataremos de resolver el problema de comunicar dos aplicaciones en Internet mediante la herramienta básica: los sockets. Existen otras alternativas de más alto nivel, como el uso del protocolo HTTP, que será estudiada en el segundo punto del capítulo. Para terminar se tratará una tercera alternativa, todavía de más alto nivel, los servicios web.
En este capítulo implementáramos el mismo ejemplo que en el capítulo anterior, es decir, almacenaremos las puntuaciones obtenidas en Asteroides, pero ahora en un servidor de Internet. Utilizaremos las tres alternativas descritas en el párrafo anterior. No obstante, has de tener claro que estos mecanismos están relacionados entre sí. Por ejemplo, si utilizas servicios web, internamente se utilizará el protocolo HTTP y además este protocolo utiliza sockets para establecer la comunicación.
Objetivos:
Repasar las alternativas principales para intercambiar datos por Internet.
Describir el uso de socket como herramienta básica de una aplicación para comunicarse con otras aplicaciones por Internet.
Mostrar cómo se programaría un protocolo basado en sockets sobre TCP.
Describir el funcionamiento del protocolo HTTP
Mostrar cómo se pueden programa peticiones HTTP desde Android.
Definir el concepto de servicio Web y comparar las alternativas más importantes.
Mostrar el acceso a servicios web de terceros desde un cliente Android.
Aprender a crear nuestro propio servidor de servicios web con PHP, Apache y MySQL.
ComunicacionesenInternetmediantesockets
Antes de definir que es unsocketconviene aclarar los roles o configuraciones que pueden tomar las aplicaciones en un proceso de comunicación. Las dos configuraciones más importantes que pueden tomar son: las llamadas “arquitectura igual a igual” y la “arquitectura cliente/servidor”. Esta segunda es la que utilizaremos en los ejemplos de este capítulo, por tanto, conviene aclarar este aspecto.
La arquitectura cliente/servidor
Las aplicaciones en Internet suelen seguir la arquitectura cliente/servidor. Esta arquitectura se caracteriza por descomponer el trabajo en dos partes (es decir dos programas): el servidor, que centraliza el servicio, y el cliente, que controla la interacción con el usuario. El servidor ha de ofrecer sus servicios a través de una dirección conocida. Algunos ejemplos de aplicaciones basadas en la arquitectura cliente/servidor son WWW o el correo electrónico. Se suelen seguir las siguientes pautas de comportamiento en esta arquitectura:
-¿Quéesunsocket?
Cada una de las diferentes aplicaciones en Internet (web, correo electrónico,…) ha de poder intercambiar información entre programas situados en diferentes ordenadores o dispositivos. Con este propósito, se va ha hacer uso del nivel de transporte de la pila de protocolos TCP/IP, cuyo objetivo final es permitir el intercambio de información a través de la red de forma fiable y transparente.
La interfaz socket define las reglas que un programa ha de seguir para utilizar los servicios del nivel de transporte en una red TCP/IP. Esta interfaz se basa en el concepto de socket. Un socket es el punto final de una comunicación bidireccional entre dos programas que intercambian información a través de Internet (socket se traduce literalmente como “enchufe”).
Dado que en un mismo dispositivo/ordenador podemos estar ejecutando de forma simultánea diferentes aplicaciones que utilizan Internet para comunicarse, resulta imprescindible identificar cada socket con una dirección diferente. Un socket se va a identificar por la dirección IP del dispositivo, más un número de puerto (de 16 bits). En Internet se suele asociar a cada aplicación un número de puerto concreto (por ejemplo: 80 para la web, 25 para el correo electrónico, 7 para ECHO o 4661 para eDonkey).
Una conexión está determinada por un par de sockets, uno en cada extremo de la conexión. Existen dos tipos de socket: socket stream y socket datagram. Veamos en qué se diferencian:
-Socketsstream(TCP)-datagram(UDP)
Socketsstream(TCP)
Lossockets streamofrecen un servicio orientado a conexión, donde los datos se transfieren como un flujo continuo, sin encuadrarlos en registros o bloques. Este tipo desocketse basa en el protocolo TCP, que es un protocolo orientado a conexión. Esto implica que antes de transmitir información hay que establecer una conexión entre los dossockets. Mientras uno de lossocketsatiende peticiones de conexión (servidor), el otro solicita la conexión (cliente). Una vez que los dossocketsestán conectados, ya se puede transmitir datos en ambas direcciones. El protocolo incorpora de forma transparente al programador la corrección de errores. Es decir, si detecta que parte de la información no llegó a su destino correctamente, esta volverá a ser trasmitida. Además, no limita el tamaño máximo de información a transmitir.
Socketsdatagram(UDP)
Los sockets datagram se basan en el protocolo UDP y ofrecen un servicio de transporte sin conexión. Es decir, podemos mandar información a un destino sin necesidad de realizar una conexión previa. El protocolo UDP es más eficiente que el TCP, pero tiene el inconveniente de que no se garantiza la fiabilidad. Además, los datos se envían y reciben en datagramas (paquetes de información) de tamaño limitado. La entrega de un datagrama no está garantizada: estos pueden duplicarse, perderse o llegar en un orden diferente del que se envió.
La gran ventaja de este tipo de sockets es que apenas introducen sobrecarga sobre la información transmitida. Además, los retrasos introducidos son mínimos, lo cual los hace especialmente interesantes para aplicaciones en tiempo real, como la transmisión de audio y vídeo sobre Internet. Sin embargo, presentan muchos inconvenientes para el programador: cuando transmitimos un datagrama no tenemos la certeza de que este llegue a su destino, por lo que, si fuera necesario, tendríamos que implementar nuestro propio mecanismo de control de errores. Otro inconveniente es el hecho de que existe un tamaño máximo de datagrama: unos 1500 bytes dependiendo de la implementación. Si la información a enviar es mayor, tendremos que fraccionarla y enviar varios datagramas independientes. En el destino tendremos que concatenarlos en el orden correcto.
En conclusión, si deseas una comunicación libre de errores y sin preocupaciones para el programador, es más conveniente que utilices sockets stream. Es el tipo de sockets que utilizaremos en los siguientes ejemplos.
-Unejemplodeuncliente/servidordeECHO
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:
telnet158.42.146.1277
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 streamdesde Android.
NOTA:Para que el ejemplo funcione en la dirección IP158.42.146.127ha 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 etiquetaandroid:id="@+id/TextView01"en elTextViewdellayoutde 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 usandoAsyncTask.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étodoejecutarCliente(). En primer lugar, todo cliente ha de conocer la dirección delsocketdel servidor; en este caso los valores se indican en el par de variablesipypuerto. Nunca tenemos la certeza de que el servidor admita la conexión, por lo que es obligatorio utilizar una seccióntry/catch. La conexión propiamente dicha se realiza con el constructor de la claseSocket. Siempre hay que tener previsto que ocurra algún problema, en tal caso, se creará la excepción y se pasará a la seccióncatch. En caso contrario, continuaremos obteniendo elInputStreamy elOutputStreamasociado alsocket. Lo cual nos permitirá obtener las variables,entradaysalidamediante 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.
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ámaloServidorECHO.
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 deServidorECHOcon 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 claseServerSocketasociado al puerto 7 para crear un socketque 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 nuevosocketcreado con este propósito, elsocketcliente. 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 enClienteECHOpor la IP de tu ordenador.El comandoipconfig(Windows) oifconfig(Linux/Mac) te permite averiguar la dirección IP de tu ordenador. No utilices como IP127.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.
[2]En el capítulo 5 se describe este problema con más detalle
-Unservidorporsocketsparalaspuntuaciones
Siguiendo la estructura básica de un cliente y un servidor TCP que acabamos de ver, va a resultar muy sencillo implementar un protocolo que permita a varios clientes conectarse a un servidor para consultar la lista de puntuaciones o mandar nuevas puntuaciones.El primer lugar, tenemos que diseñar un protocolo que permita realizar las dos operaciones.
Para consultar puntuaciones el cliente se conectará al servidor y le mandará los caracteres PUNTUACIONES (solo se permite en mayúsculas) seguido de un salto de línea. El servidor mandará todo el listado de puntuaciones, separadas por caracteres de salto de línea. A continuación, se cerrará la conexión.
Cliente:PUNTUACIONES
Servidor:19000 Pedro Perez
17500 María Suarez
13000 Juan García
Para almacenar una nueva puntuación el cliente se conectará al servidor y mandará un texto con la puntuación obtenida, seguido de un salto de línea. El servidor lo reconocerá como una nueva puntuación siempre que este texto no seaPUNTUACIONES. En tal caso almacenará la puntuación y mandará los caracteresOKseguidos de un salto de línea. A continuación se cerrará la conexión.
Cliente:32000 Eva Gutierrez
Servidor:OK
En segundo lugar, hay que elegir un número de puerto para realizar la comunicación; por ejemplo el 1234.
Ejercicio paso a paso:Estudio del protocolo PUNTUACIONES utilizando 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 IP158.42.146.127ha de haber un servidor de PUNTUACIONES en funcionamiento. En caso de no ser así, implementar tu propio servidor de PUNTUACIONES,tal y como se muestra en uno de los siguientes ejercicios.
1.Para conectarse al servidor desde un intérprete de comandos (símbolo del sistema/shell) escribe:
telnet158.42.146.1271234
2.Si se establece la conexión, escribe un número seguido de tu nombre y pulsa <Intro>. Por ejemplo:
14000 Juan García
3.La respuesta obtenida ha de serOKy luego se cerrará la conexión.
4.Repite el punto 2 y tras la conexión escribe:
PUNTUACIONES
5.La respuesta obtenida ha de ser la lista de puntuaciones donde ha de estar la que acabas de introducir.
Ejercicio paso a paso:Almacenando las puntuaciones mediante un protocolo basado en sockets
1.Crea un nuevo proyecto Java (Java SE, no para Android) y llámalo ServidorPuntuacionesSocket. Este proyecto ha de contener la clase ServidorPuntuaciones. Instrucciones detalladas se muestran en el ejercicio Un servidor de ECHO.
2.Reemplaza en código por el que se muestra a continuación:
public class ServidorPuntuaciones {
public static void main(String args[]) {
Vector<String> puntuaciones = new Vector<String>();
try {
ServerSocket s = new ServerSocket(1234);
System.out.println("Esperando conexiones...");
while (true) {
Socket cliente = s.accept();
BufferedReader entrada = new BufferedReader(
new InputStreamReader(cliente.getInputStream()));
PrintWriter salida = new PrintWriter(new OutputStreamWriter(
cliente.getOutputStream()), true);
String datos = entrada.readLine();
if (datos.equals("PUNTUACIONES")) {
for (int n = 0; n < puntuaciones.size(); n++) {
salida.println(puntuaciones.get(n));
}
} else {
puntuaciones.add(0, datos);
salida.println("OK");
}
cliente.close();
}
} catch (IOException e) {
System.out.println(e);
}
}
}
3.Ejecuta el proyecto
4.Verifica que en la vistaRun aparece:"Esperando conexiones..."
5.Desde la vistaRun podrás detener la aplicación pulsando en el cuadrorojo.
NOTA:Si ejecutas de nuevo la aplicación sin pararla primero, dará un error. Esto es debido a que la aplicación ya lanzada no es detenida y esta aplicación tiene asociado el puerto 1234. El sistema no permitirá que una nueva aplicación escuche este puerto.
6.Abre el proyecto Asteroides y crea la siguiente clase:
public class AlmacenPuntuacionesSocket implements AlmacenPuntuaciones{
public AlmacenPuntuacionesSocket() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.
Builder().permitNetwork().build());
}
public void guardarPuntuacion(int puntos, String nombre, long fecha){
try {
Socket sk = new Socket("X.X.X.X", 1234);
BufferedReader entrada = new BufferedReader(
new InputStreamReader(sk.getInputStream()));
PrintWriter salida = new PrintWriter(
new OutputStreamWriter(sk.getOutputStream()),true);
salida.println(puntos + " " + nombre);
String respuesta = entrada.readLine();
if (!respuesta.equals("OK")) {
Log.e("Asteroides", "Error: respuesta de servidor incorrecta");
}
sk.close();
} catch (Exception e) {
Log.e("Asteroides", e.toString(), e);
}
}
public Vector<String> listaPuntuaciones(int cantidad) {
Vector<String> result = new Vector<String>();
try {
Socket sk = new Socket("X.X.X.X", 1234);
BufferedReader entrada = new BufferedReader(
new InputStreamReader(sk.getInputStream()));
PrintWriter salida = new PrintWriter(
new OutputStreamWriter(sk.getOutputStream()),true);
salida.println("PUNTUACIONES");
int n = 0;
String respuesta;
do {
respuesta = entrada.readLine();
if (respuesta != null) {
result.add(respuesta);
n++;
}
} while (n < cantidad && respuesta != null);
sk.close();
} catch (Exception e) {
Log.e("Asteroides", e.toString(), e);
}
return result;
}
}
7.Sustituiye las dos apariciones de"X.X.X.X"por la dirección IP donde esté ejecutándose el servidor.
NOTA:El comandoipconfig(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.
8.Recuerda que ahora la aplicaciónAsteroidesnecesita el permisoINTERNET. Y tiene que ser compilada con una versión mínima 9 (para StrictMode).
9.Ejecuta la aplicación y accede a visualizar la lista de puntuaciones. Luego inicia una partida nueva.
10.Verifica que en la vistaConsolaaparecen las consultas al servidor.
11.Para terminar, reemplaza la IP por la siguiente"158.42.146.127"para conectarte a un servidor compartido.
NOTA:Es posible que este servicio no haya sido iniciado.
12.Modifica el código correspondiente para que la nueva clase pueda ser seleccionada como almacén de las puntuaciones.
13.Comprueba si otros usuarios han accedido a este servidor y aparecen sus puntuaciones.
Dentro del mundo de Internet destaca una aplicación que es, con mucho, la más utilizada: la World Wide Web (WWW), a la que nos referiremos coloquialmente como la web. Su gran éxito se debe a la facilidad de uso, dado que simplifica el acceso a todo tipo de información, y a que esta información es presentada de forma atractiva. Básicamente, la web nos ofrece un servicio de acceso a información distribuida en miles de servidores en todo Internet, que nos permite ir navegando por todo tipo de documentos multimedia gracias a un sencillo sistema de hipervínculos.
Para la comunicación entre los clientes y los servidores de esta aplicación, se emplea el protocolo HTTP (Hypertext Transfer Protocol), que será el objeto de estudio de este apartado.
-ElprotocoloHTTP
HTTP es un sencillo protocolo cliente-servidor que articula los intercambios de información entre los navegadores web y los servidores web. Fue propuesto por Tim Berners-Lee, atendiendo a las necesidades de un sistema global de distribución de información como la World Wide Web. En la web los servidores han de escuchar en el puerto 80, esperando la conexión de algún cliente web.
A continuación describimos los pasos habituales que se siguen en una interacción del protocolo:
1)El usuario quiere acceder a la páginahttp://www.upv.es/dir/pag.html. Para lo cual pincha en un enlace de un documento HTML o introduciendo directamente en el campoDireccióndel navegador Web.
2)El navegador averigua la dirección IP dewww.upv.es.
3)El navegador establece una conexión con el puerto 80 de esta IP.
4)El navegador envía por esta conexión (¿carácter de salto de línea):
GET /dir/pag.html
5)El servidor envía la página a través de la conexión:
<HTML>
<HEAD>
<TITLE>Página de ... </TITLE>
...
</HTML>
6) El servidor cierra la conexión.
Este proceso se repite cada vez que el navegador necesita un fichero del servidor. Por ejemplo, si se ha bajado un documento HTML en cuyo interior están insertadas cuatro imágenes, el proceso anterior se repite un total de cinco veces, una para el documento HTML y cuatro para las imágenes.
Como ves, se trata de un protocolo sin estado. Cada petición contiene la información necesaria para ser atendida. Si deseamos mantener un estado tendrá que ser implementado usando algún mecanismo adicional (por ejemplo las cookies).
Ejercicio paso a paso:Estudio del protocolo HTTP/0.9 utilizando el comando Telnet.
En caso de que el servidor no responda, puedes realizar el ejercicio con cualquier página de otro servidor. El carácter ~ se obtiene pulsando simultáneamente <Alt Gr> y <4>.
2.Visualiza el contenido HTML de la página (menú “Ver/Código fuente”, “Herramientas/ Ver código fuente”, o similar).
3.Desde un intérprete de comandos (símbolo del sistema/shell) escribe:
telnet www.dcomg.upv.es 80
Este comando permite establecer una conexión TCP con el puerto 80 del servidor. A partir de ahora todo lo que escribas será enviado al servidor (aunque en mucho casos no lo veas en pantalla) y todo lo que el servidor envíe será impreso en pantalla..
NOTA: Si utilizas un servidor diferente, asegúrate de que soporta la versión HTTP/0.9.
4.Cuando se establezca la conexión teclea exactamente:
GET /~jtomas/corta.html
Si te equivocas no uses la tecla de borrar. En tal caso, repite el ejercicio desde el punto 3.
5.Observa que la respuesta obtenida coincide con el contenido HTML del paso 2.
-Versión1.0delprotocoloHTTP
Con la popularización de la aplicación WWW, pronto se vio la necesidad de ampliar este sencillo protocolo para permitir nuevas funcionalidades. Se definió la versión 1.0 del protocolo que añadía nuevos métodos (PUT, POST,..) además de permitir el intercambio de cabeceras entre cliente y servidor.
A continuación se muestra un ejemplo de interacción para la versión 1.0:
Cliente:GET /dir/pag.htmlHTTP/1.0
User-Agent:Internet Explorer v3.2
Host:www.upv.es
Accept:text/html, image/gif, image/jpeg
<línea en blanco>
Servidor:HTTP/1.1 200 OK
Server:Microsoft-IIS/5.0
Last-Modified:Mon, 25 Feb 2002 15:49:22 GMT
Content-Type:text/html
<línea en blanco>
<HTML>
<HEAD>
<TITLE>Página de ... </TITLE>
...
</HTML>
En esta nueva versión, el navegador se conecta al puerto 80 del servidor y normalmente le envía el comando “GET” seguido de la página que desea obtener. Ahora el navegador añadirá la palabra “HTTP/1.0” para indicar al servidor que quiere utilizar esta nueva versión del protocolo. A continuación, el navegador introducirá un salto de línea seguido, opcionalmente, de alguna cabecera (véase RFC 2616). Una cabecera consta de un identificador de cabecera, seguido de dos puntos y el valor de la cabecera, más un salto de línea (¿). Estas cabeceras permitirán que el navegador indique al servidor ciertos datos que pueden serle de utilidad. Por ejemplo, con el identificador de cabecera “User-Agent:” se puede indicar el tipo y la versión de navegador con el que se realiza la solicitud. “Host:” permite indicar el ordenador donde se ejecuta el servidor. O la cabecera “Accept:”, que permite indicar al servidor qué tipo de documentos es capaz de visualizar el navegador en formato MIME. Cuando el navegador ya no quiera insertar más cabeceras, introducirá una línea en blanco. Es decir, tras el salto de línea de la última cabecera, manda un nuevo salto de línea.
Cuando el servidor lea una línea en blanco, es decir, dos saltos de línea seguidos, sabrá que le ha llegado su turno y tiene que contestar. En esta versión del protocolo el servidor no transmitirá la página solicitada directamente. Antes contesta con una línea indicando: primero la versión más alta que soporta (normalmente “HTTP/1.1”), a continuación un espacio y un código de tres dígitos que informa de si se puede realizar la operación solicitada, finalizando con una frase explicativa sobre este código. Algunos códigos de respuesta posibles son: 200 OK, 401 no autorizado, 404 fichero no encontrado, etc. Tras esta primera línea el servidor podrá enviar alguna cabecera con información sobre el servidor o el documento que va a transmitir. Cuando ya no quiera insertar más cabeceras introducirá una línea en blanco seguida del documento.
Aunque el método GET es el más utilizado, en la versión 1.0 se añaden nuevos métodos. A continuación se incluye una descripción:
GET: Petición de lectura de un recurso.
POST: Envío de información asociada a un recurso del servidor.
PUT: Creación de un nuevo recurso en el servidor.
DELETE: Eliminación de un recurso.
HEAD: El servidor solo transmitirá las cabeceras, no la página
Ejercicio paso a paso:Estudio del protocolo HTTP v1.0 utilizando el comando telnet.
1.Desde un intérprete de comandos (símbolo del sistema/shell) escribe:
telnet www.dcomg.upv.es 80
2.Cuando se establezca la conexión teclea:
GET /~jtomas/corta.htmlHTTP/1.0
3.Observa que la respuesta obtenida es similar al ejercicio anterior, pero ahora el servidor ha incluido cabeceras. ¿Qué información puedes sacar de estas cabeceras? ¿Por qué has tenido que introducir dos saltos de línea?
4.Repite el ejercicio utilizando el comando HEAD.
Aunque la versión más reciente es HTTP/1.2 (y existe un borrador de HTTP/2.0), la versión más utilizada en la actualidad es HTTP/1.1. Incorpora algunas mejoras, como las conexiones persistentes, activadas por defecto o la gestión de la caché del cliente. También permite al cliente enviar múltiples peticiones a la vez.
-UtilizandoHTTPdesdeAndroid
Tras el gran éxito de la web, el protocolo HTTP está siendo utilizado con finalidades diferentes de las que tenía en un principio: la descarga de páginas web. Por ejemplo, hoy en día es frecuente su uso para el intercambio de ficheros, la emisión de vídeo o la comunicación entre aplicaciones. A continuación describiremos las herramientas disponibles en Android para utilizar el protocolo HTTP. Para este propósito tenemos dos alternativas principales desde Android: el uso de las librerías java.net.* o org.apache.commons.httpclient.*. En el siguiente ejemplo utilizaremos las primeras.
En el ejemplo de este apartado vamos a extraer información de una de las páginas web más utilizadas en la actualidad: el servicio de búsqueda de Google. En concreto, nos interesa conocer el número de apariciones de una determinada secuencia de palabras en la web. En ciertas ocasiones esta información puede resultar muy interesante. Por ejemplo, tenemos dudas sobre el uso de una preposición en inglés: ¿se escribe travel in bus o travel by bus? Si buscamos ambas secuencias de palabras en Google, hay que ponerlas entre comillas para que busque la secuencia de forma literal. Obtenemos 240.000 apariciones para la primera y 1,1 millones para la segunda. Nuestra duda ha sido resuelta. Esta aplicación también puede utilizarse para averiguar quién es más popular en Internet, Antonio Banderas o Rafael Nadal.
Básicamente la aplicación que mostramos a continuación funciona de la siguiente forma:
1) El usuario introduce una secuencia de palabras, por ejemplo “Antonio Banderas”.
2) Accedemos al servidor mediante la siguiente URL:
En este caso las peticiones se atienten mediante el método GET. En este método si queremos enviar información al servidor hemos de incluirla tras un carácter “?” seguido de un nombre de parámetro seguido del carácter “=” seguido del valor. Los diferentes parámetros se separan mediante el carácter “&”. Los espacios en blanco han de ser sustituidos por el carácter “+”. En este ejemplo el parámetro hl corresponde al idioma de búsqueda y q a las palabras a buscar.
3) Obtenemos la respuesta del servidor en una variable de tipo string.
4) Buscamos la primera aparición de “Aproximadamente” en la respuesta.
5) Tras esta palabra se encuentra la información que buscamos.
Obviamente, el correcto funcionamiento de esta aplicación está sujeto a que no se produzcan cambios en la forma en que Google visualiza los resultados. En caso de que se realice algún cambio en esta página, va a ser necesaria una adaptación de nuestra aplicación. La técnica de sacar información directamente de una página web se conoce como web scraping. Hoy en día existe otra alternativa más fiable para obtener información. En el siguiente apartado mostraremos cómo utilizar un servicio web con este propósito.
Ejercicio paso a paso:Búsquedas en Google con HTTP
1.Crea un nuevo proyecto con los siguientes datos:
Application name: HTTP
Package name: com.example.http
☑Phone and Tablet
Minimum SDK: API 9 Android 2.3 (Gingerbread)
Add an activity: Empty Activity
NOTA: Utilizamos la versión mínima la 2.3 para poder configurar StrictMode
2.La aplicación ha de solicitar el permiso de acceso a Internet, añadiendo en AndroidManifest.xml la siguiente línea:
La apariencia de esteLayoutse muestra a continuación:
4.Reemplaza el código deMainActivity.javapor:
public class MainActivity extends Activity {
private EditText entrada;
private TextView salida;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
entrada = (EditText) findViewById(R.id.EditText01);
salida = (TextView) findViewById(R.id.TextView01);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.
Builder().permitNetwork().build());
}
public void buscar(View view){
try {
String palabras = entrada.getText().toString();
String resultado = resultadosGoogle(palabras);
salida.append(palabras + "--" + resultado + "\n");
} catch (Exception e) {
salida.append("Error al conectar\n");
Log.e("HTTP", e.getMessage(), e);
}
}
String resultadosGoogle(String palabras) throws Exception {
String pagina = "", devuelve = "";
URL url = new URL("http://www.google.es/search?hl=es&q=\""
+ URLEncoder.encode(palabras, "UTF-8") + "\"");
HttpURLConnection conexion = (HttpURLConnection)
url.openConnection();
conexion.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 6.1)");
if (conexion.getResponseCode()==HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new
InputStreamReader(conexion.getInputStream()));
String linea = reader.readLine();
while (linea != null) {
pagina += linea;
linea = reader.readLine();
}
reader.close();
devuelve = buscaAproximadamente(pagina);
} else {
devuelve = "ERROR: " + conexion.getResponseMessage();
}
conexion.disconnect();
return devuelve;
}
String buscaAproximadamente(String pagina){
int ini = pagina.indexOf("Aproximadamente");
if (ini != -1) {
int fin = pagina.indexOf(" ", ini + 16);
return pagina.substring(ini + 16, fin);
} else {
return "no encontrado";
}
}
}
El comienzo del código ha de resultarte familiar hasta la última línea del método onCreate(). En esta línea se configura StrictMode[1]. para que permita accesos a la red desde el hilo principal.
Pasemos a describir el método resultadosGoogle(). Este método toma como entrada una secuencia de palabras y devuelve el número de veces que Google las ha encontrado en alguna página web. Lo primero que llama la atención es el modificador throws Exception. Estamos obligados a incluirlo si utilizamos la clase HttpURLConnection. Este modificador obliga a utilizar el método dentro de una sección try … catch …. La razón de esto es que toda conexión HTTP es susceptible de no poder realizarse, por lo que tenemos la obligación de tomar las acciones pertinentes en caso de problemas.
Tras la declaración de variables, creamos la URL que utilizaremos para la conexión. El método URLEncoder.encode() se encargará de codificar las palabras en el formato esperado por el servidor. Entre otras cosas reemplaza espacios en blanco, caracteres no ASCII, etc. A continuación, preparamos la conexión por medio de la clase HttpURLConnection. Mediante el método setRequestProperty() podemos añadir cabeceras HTTP. En el punto 7 de este ejercicio se demuestra la necesidad de insertar la cabecera User-Agent.
En la siguiente línea se utiliza el método getResponseCode() para establecer la conexión. Si se establece sin problemas (HTTP_OK) leemos la respuesta línea a línea, concatenándola en el stringpagina. El método buscaAproximadamente() busca en pagina la primera aparición de la palabra “Aproximadamente”. Si la encontramos, buscamos el primer espacio a continuación del número que ha de aparecer tras “Aproximadamente” y devolvemos los caracteres entre ambas posiciones. En caso de no encontrar “Aproximadamente”, puede que la secuencia buscada no se haya encontrado, aunque también es posible que Google haya cambiado la forma de devolver los resultados. En el caso de que la conexión no haya sido satisfactoria, devolvemos el mensaje de respuesta que nos dio el servidor getResponseMessage().
5.Ejecuta la aplicación y verifica que funciona correctamente.
6.En el código anterior comenta la línea:
conexion.setRequestProperty("User-Agent", ...);
7.Ejecuta el programa. Ahora el resultado ha de ser:
ERROR: Forbidden
En este caso, al tratar de establecer la conexión el servidor, en lugar de devolvernos el código de respuesta 200: OK, nos ha devuelto el código 403: Forbidden. ¿Por qué? La cabecera User-Agent informa al servidor de qué tipo de cliente ha establecido la conexión. Según se demuestra en este ejercicio, el servicio de búsquedas de Google prohíbe la respuesta a aquellos clientes que no se identifican.
8.Analiza el valor asignado a la cabecera“Mozilla/4.0 ...”. Puedes comprobar que la información que estamos dando al servidor es totalmente errónea.
9.Modifica este valor por“Ejemplo de El gran libro de Android”y comprueba el resultado es 403: Forbidden¿Por qué no quiere responder? La respuesta es que desde finales de 2011, el servidor de Google exige que el tipo de navegador que se conecte sea conocido.
UsodeHTTPconAsyncTask
En el ejercicio anterior hemos configurado el modo estricto para que nos permita realizar accesos a la red desde el hilo principal. Aunque en la mayoría de los casos la interacción con el servidor es inferior a 2 décimas de segundo, podrían darse algunos casos en que la interacción fuera superior a un segundo (servidores sobrecargados o redes muy lentas). En estos casos, la interfaz de usuario permanecería bloqueada un tiempo excesivo.
En el capítulo 5 hemos aprendido a utilizar la clase AsyncTaskpara resolver estos problemas. El siguiente ejercicio nos muestra cómo puede utilizarse en este caso:
Ejercicio:Búsquedas en Google con HTTP y AsyncTask
1.Añade la siguiente clase dentro de MainActivitydel ejercicio anterior:
Observa como la nueva clase extiende AsyncTask<String, Void, String>. Se han escogido estos tres tipos de datos porque la entrada de la tarea será un String con las palabras que hay que buscar; no se necesita información de progreso y la salida será un String con el número de veces que se encuentran. En el método onPreExecute() se visualiza un ProgressDialog con estilo SPINNER. El método doInBackground() es el único que se ejecuta en un nuevo hilo. En el nos limitamos a llamar al método que habíamos programado en el ejercicio anterior para hacer la consulta. En caso de que se produzca una excepción cancelaremos la tarea, mostraremos el error en el Log y no devolveremos nada. El resto de los métodos se llaman según la tarea haya concluido o haya sido cancelada.
2.Añade el siguiente método:
public void buscar2(View view){
String palabras = entrada.getText().toString();
salida.append(palabras + "--");
new BuscarGoogle().execute(palabras);
}
3.Abre el layout activity_main.xml y añade un botón con texto: “buscar en Google con AsyncTask” y con un valor para onClick: “buscar2”.
4.Verifica que la aplicación funciona correctamente.
La W3C define "servicio web" como un sistemade software diseñado para permitir interoperabilidad máquina a máquina en una red. Se trata de API que son publicadas, localizadas e invocadas a través de la web. Es decir, una vez desarrolladas, son instaladas en un servidor, y otras aplicaciones (u otros servicios web) pueden descubrirlas desde otros ordenadores de Internet e invocar uno de sus servicios.
Como norma general, el transporte de los datos se realiza a través del protocolo HTTP y la representación de los datos mediante XML. Sin embargo, no hay reglas fijas en los servicios web y en la práctica no tiene por qué ser así.
Una de las grandes ventajas de este planteamiento es que es tecnológicamente neutral. Es decir, podemos utilizar un servicio web sin importarnos el sistema operativo o el lenguaje en el que fue programado. Además, al apoyarse sobre el protocolo HTTP, puede utilizar los sistemas de seguridad (https) y presenta pocos problemas con cortafuegos, al utilizar puertos que suelen estar abiertos (80 o 8080).
Como inconveniente podemos resaltar que, dado que el intercambio de datos se realiza en formato de texto (XML), tiene menor rendimiento que otras alternativas como RMI (Remote Method Invocation), CORBA (Common Object Request Broker Architecture) o DCOM (Distributed Component Object Model). Además, el hecho de apoyarse en HTTP hace que resulte complicado para un cortafuego filtrar este tipo de tráfico. ¿No acabamos de decir que esto era una ventaja? Es posible que lo que para un desarrollador sea una ventaja, para un administrador de red sea un inconveniente.
-Alternativasenlosserviciosweb
Como acabamos de ver, el término “servicio web” resulta difícil de definir de forma precisa. En torno a este concepto se han desarrollado varias propuestas bastante diferentes entre sí. En este apartado estudiaremos las dos alternativas que están teniendo más relevancia en la actualidad: SOAP y REST. No obstante, dada la complejidad que surge de estas propuestas, resulta interesante centrar algunos conceptos antes de empezar a describir estas alternativas. Comenzaremos indicando que existen tres enfoques diferentes a la hora de definir un servicio web. Es lo que se conoce como arquitectura del servicio web.
Llamadas a procedimiento remotos (RPC): Se enfoca el servicio web como una colección de operaciones o procedimientos que pueden ser invocados desde una máquina diferente de donde se ejecutan. Como RPC es una extensión directa del paradigma de llamadas a funciones, resulta sencillo de entender para un programador. Al ser una de las primeras alternativas que se implementó, se conocen como servicios web de primera generación.
Arquitectura orientada a servicios (SOAP): En el planteamiento anterior, RPC, la unidad básica de interacción es la operación. En este nuevo planteamiento, la unidad de interacción pasa a ser el mensaje. Por lo tanto, en muchos casos se conocen como servicios orientados a mensaje. Cada uno de los mensajes que vamos a utilizar ha de ser definido siguiendo una estricta sintaxis expresada en XML. En la actualidad se trata de la arquitectura más extendida, soportada por la mayoría del software de servicios web.
Transferencia de estado representacional(REST): En los últimos años se está popularizando este nuevo planteamiento, que se caracteriza principalmente por su simplicidad. En REST se utiliza directamente el protocolo HTTP, por medio de sus operaciones GET, POST, PUT y DELETE. En consecuencia, esta arquitectura se centra en la solicitud de recursos, en lugar de las operaciones o los mensajes de las alternativas anteriores.
-ServicioswebbasadosenSOAP
SOAP (Simple Object Access Protocol) es el protocolo más utilizado en la actualidad para implementar servicios web. Fue creado por Microsoft, IBM y otros, aunque en la actualidad está bajo el auspicio de la W3C.
Utiliza como transporte HTTP, aunque también es posible utilizar otros métodos de transporte, como el correo electrónico. Los mensajes del protocolo se definen utilizando un estricto formato XML, que ha de ser consensuado por ambas partes. A continuación se muestra un posible ejemplo de mensaje SOAP
Un mensaje SOAP contiene una etiqueta <Envelope>, que encapsula las etiquetas <Header> y <Body>. La etiqueta <Header> es opcional y encapsula aspectos relativos a calidad del servicio, como seguridad, esquemas de direccionamiento, etc. La cabecera <Body> es obligatoria y contiene la información que las aplicaciones quieren intercambiar.
SOAP proporciona una descripción completa de las operaciones que puede realizar un nodo mediante una descripción WSDL (Web Services Description Language), por supuesto codificada en XML. En uno de los siguientes apartados crearemos un servicio web y podremos estudiar el fichero WSDL correspondiente.
Aunque SOAP está ampliamente extendido como estándar para el desarrollo de servicios web, no resulta muy adecuado para ser utilizado en Android. Esto es debido a la complejidad introducida, supone una sobrecarga que implica un menor rendimiento frente a otras alternativas como REST. Además, Android no incorpora las librerías necesarias para trabajar con SOAP.
No obstante, es posible que ya dispongas de un servidor basado en SOAP y necesites implementar un cliente en Android. En tal caso puedes utilizar las librerías kSOAP2 (http://ksoap2.sourceforge.net/).
-ServicioswebbasadosenREST
En primer lugar conviene destacar que el término REST se refiere a una arquitectura en lugar de a un protocolo en concreto, como es el caso de SOAP. A diferencia de SOAP, no vamos a añadir una capa adicional a la pila de protocolos, sino que utilizaremos directamente el protocolo HTTP. Siendo estrictos, la arquitectura REST no impone el uso de HTTP; no obstante, en la práctica se entiende que un servicio web basado en REST es aquel que se implementa directamente sobre la web.
Este planteamiento supone seguir los principios de la aplicación WWW, pero en lugar de solicitar páginas web solicitaremos servicios web. Los principios básicos de la aplicación WWW y, por tanto, los de REST son:
Transporte de datos mediante HTTP, utilizando las operaciones de este protocolo, que son GET, POST, PUT y DELETE.
Los diferentes servicios son invocados mediante el espacio de URI unificado. Como ya se ha tratado en este libro, una URI identifica un recurso en Internet. Este sistema ha demostrado ser flexible, sencillo y potente al mismo tiempo. Se cree que fue uno de los principales factores que motivó el éxito de WWW.
La codificación de datos es identificada mediante tipos MIME (text/html, image/gif, etc.), aunque el tipo de codificación preferido es XML (text/xml).
Ejercicio paso a paso:Comparativa entre una interacción SOAP y REST.
La empresa WebserviceX.NET ofrece un servicio web, GetIPService, que nos permite conocer, a partir de una dirección IP, el país al que pertenece. Este servicio puede ser utilizado tanto con REST como con SOAP, lo cual nos va a permitir comparar ambos mecanismos. Más todavía, dentro de REST tenemos dos opciones para mandar datos al servidor: el método GET y el método POST. El servicio que vamos a probar nos permite las dos variantes, lo que nos permitirá comparar ambos mecanismos.
3.Prueba otras IP al azar y verifica a que países pertenecen.
4.Vamos a emular el protocolo HTTP de forma similar a como lo hemos hecho en secciones anteriores. Desde un intérprete de comandos (símbolo del sistema/shell) escribe:
telnet www.webservicex.net 80
5.Cuando se establezca la conexión teclea exactamente el siguiente código seguido de un salto de línea adicional:
GET /geoipservice.asmx/GetGeoIP?IPAddress=158.42.38.1 HTTP/1.1
Host: www.webservicex.net
NOTA:También puedes cortar el texto y pegarlo. Para pegar sobre la ventana de símbolo de sistema de Windows has de pulsar con el botón derecho del ratón y seleccionar Pegar.
6.El resultado ha de parecerse al anterior aunque al principio el servidor enviará sus cabeceras:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Length: 374
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Jan 2012 19:28:55 GMT
7.Como acabamos de ver, el protocolo HTTP permite enviar información al servidor utilizando el método GET e introduciendo un carácter “?” al final de la URL seguido de los parámetros. El protocolo HTTP también permite mandar información con el método POST. El servicio web que estamos utilizando nos permite las dos alternativas. Veamos en qué consiste el método POST. Escribe en el intérprete de comandos:
telnet www.webservicex.net 80
8.Cuando se establezca la conexión pega los siguientes caracteres:
POST /geoipservice.asmx/GetGeoIP HTTP/1.1
Host: www.webservicex.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 21
IPAddress=158.42.38.1
Como puedes observar la información enviada es la misma, aunque ahora en lugar de adjuntarla a la URL se manda tras las cabeceras separada por una línea en blanco.
NOTA: La cabeceraContent-Length:es obligatoria. Indica el número de caracteres enviados. En caso de que cambiara la longitud de la dirección IP tendrías que ajustarlo.
9.El servidor está esperando nuevos comandos. Pulsa <Ctrl-C> para cerrarla conexión.
10.Ahora vamos a usar el mismo servicio aunque mediante el protocolo SOAP 1.1(NOTA:también es posible utilizar SOAP 1.2).Escribe en el interprete de comandos:
telnet www.webservicex.net 80
11.Cuando se establezca la conexión pega los siguientes caracteres:
12.Pulsa <Ctrl-C> para cerrar la conexión. Compara la información mandada en SOAP con el caso anterior. El resultado obtenido ha de ser similar al siguiente:
Portal que recopila datos en abierto de administraciones españolas: ministerios, comunidades… http://datos.gob.es/
Gobierno de España
SOAP /REST (XML,…)
Además de estas, te recomendamos que visites la web de la empresa WebserviceX.NET, que ofrece decenas de servicios web gratuitos. Otro sitio interesante es el que ofrece el Ministerio de Fomento de España[1]. Aquí encontrarás un directorio con centenares de servicios web geográficos ofrecidos por instituciones públicas (por ejemplo, el precio del combustible en estaciones de servicio).
En este apartado nos centraremos en cómo utilizar un servicio REST publicado por un tercero. Como se ha comentado el acceso a un servicio SOAP resulta algo más complicado y Android no dispone de librerías para facilitarnos el trabajo.
Ejercicio:Acceso a servicios Web de búsqueda de libros.
En concreto vamos a utilizar el servicio Books API, que permite buscar y gestionar libros de la base de datos de Google. Es un servicio obsoleto, aunque actualmente operativo. No usaremos el nuevo servicio que ofrece Google (nueva Books API), dado que este nos da el resultado en JSON en lugar de en XML y en este libro no hemos estudiado el trabajo con JSON.
1.Para probar el servicio Web abre un navegador web y accede a la siguiente URL:
En el resultado obtenido localiza la etiquetatotalResults. Representan los libros encontrados que contienen la búsqueda “antonio banderas”.
2.Abre el proyecto HTTP creado en el ejercicio “Utilizando HTTP desde Android”. Ahora utilizaremos un servicio web de búsqueda de libros, en lugar de buscar la información en una página web.
3.En la actividad principal añade el siguiente método:
El método comienza creando la URL de acceso. El resto del código utiliza las libreríasorg.xml.sax.* para realizar un proceso de parser sobre el código XML de la URL y así extraer la información que nos interesa. Este proceso se ha explicado en el capítulo anterior. El trabajo que sí que tendremos que realizar en función del formato XML específico será la creación de la clase XMLHandler. Una vez finalizado el parser, podemos llamar al método getTotalResults() de nuestro manejador para que nos devuelva la información que nos interesa.
4.Realiza una copia del método buscar() cambiando el nombre por buscar3(). En este método reemplaza resultadosGoogle() por resultadosSW().
5.Abre el layout activity_main.xml y añade un botón con texto: “buscar en SW libros” y con un valor para onClick: “buscar3”.
6.A continuación mostramos la definición de la clase ManejadorXML. Copia este código dentro de la clase MainActivity:
public class ManejadorXML extends DefaultHandler {
private String totalResults;
private StringBuilder cadena = new StringBuilder();
public String getTotalResults() {
return totalResults;
}
@Override
public void startElement(String uri, String nombreLocal, String
nombreCualif, Attributes atributos) throws SAXException {
cadena.setLength(0);
}
@Override
public void characters(char ch[], int comienzo, int lon){
cadena.append(ch, comienzo, lon);
}
@Override
public void endElement(String uri, String nombreLocal,
String nombreCualif) throws SAXException {
if (nombreLocal.equals("totalResults")) {
totalResults = cadena.toString();
}
}
}
Para procesar el fichero XML extendemos la clase DefaultHandler y rescribimos muchos de sus métodos: en startElement() inicializamos la variablecadena cada vez que empieza una etiqueta; en el método characters() añadimos el contenido que aparece dentro de la etiqueta; finalmente, en endElement() recogemos el valor acumulado en cadena cada vez que termina una etiqueta. Como hemos comentado, de todo el código XML que vamos a procesar, solo nos interesa el contenido de la etiqueta <totalResults>.
7.Verifica el funcionamiento de la aplicación.
Práctica: Convertidor de divisas mediante un servicio web
Uno de los servicios Web, ofrecidos en Yahoo Finance API, permite obtener la ratio de conversión de divisas según el cambio actual. A continuación se muestra un ejemplo de uso para obtener la ratio euro-dólar:
1. Retoma el diseño de layouts de la eurocalculadora, realizado en el capítulo 2, para un nuevo proyecto de conversión de divisas. En una primera fase solo se realizará la conversión de euros a dólares, aplicando la ratio obtenida a través del servicio web indicado.
2. En una segunda fase puedes permitir que el usuario seleccione la divisa de entrada (reemplazando en la URL “EUR”) y la de salida (reemplazando en la URL “USD”). Puedes encontrar una lista de las divisas disponibles en [1].
A la hora de desarrollar una aplicación distribuida, una de las alternativas más utilizadas en la actualidad son los servicios web. A lo largo de este apartado y el siguiente aprenderás cómo crear tus propios servicios web e instalarlos en un servidor web. Como no podría ser de otra manera, el ejemplo seleccionado es el mismo que ya hemos implementado en varias ocasiones: un almacén de las mejores puntuaciones de Asteroides.
No resultaría demasiado complicado rescribir el ejemplo del servidor de ECHO descrito en un apartado anterior para crear nuestro propio servidor web. No obstante, si estamos trabajando en un entorno empresarial, esta alternativa no sería la más adecuada. En este entorno es mucho más recomendable utilizar un servidor web de uso comercial, como Apache.Esta solución resulta mucho más segura y escalable.
En el siguiente apartado aprenderemos a crear un servicio web usando la combinación Apache, Tomcat y Axis2. Esta opción tiene varias ventajas: toda la programación la hacemos con un mismo lenguaje (Java), el código que tenemos que escribir es muy limpio y permite publicar el servicio con estilo REST o SOAP.
En este apartado estudiaremos otra alternativa, que consiste en usar el trinomio Apache, PHP y MySQL. Tiene sus inconvenientes, como la necesidad de usar un nuevo lenguaje (PHP), y el código a usar es más rudimentario. Sin embargo, presenta importantes ventajas: se trata de la solución más extendida. La mayoría de las empresas ya disponen de un servidor web basado en Apache, PHP y MySQL. Siempre será conveniente montar el servicio web usando la misma tecnología que la que usamos en el sitio web. Por otra parte, la mayoría de los servidores de hosting comerciales trabajan con PHP y MySQL.
Ejercicio:Instalación de Apache, PHP y MySQL
En este ejercicio vamos a instalar en nuestro propio ordenador el servidor web Apache con su extensión para poder ejecutar código PHP. Además del servidor de bases de datos MySQL. Este proceso puede realizarse de forma muy sencilla y rápida utilizando el paquete de software XAMPP. Además incorpora algunas herramientas para poder administrar estos servidores muy fácilmente. No obstante, usar un servidor de hosting comercial puede ser una alternativa más sencilla y segura. Si no estás interesado en instalar tu propio servidor web, puedes saltarte este ejercicio y realizar el ejercicio que encontrarás más adelante.
1.Descarga la última versión de XAMPP de la siguiente web:
Encontrarás versiones para Linux, Windows y Mac. En este ejercicio hemos instalado la versión 1.8.2 para Windows usando el ejecutable.
2.Inicia el proceso de instalación según tu SO. Los componentes mínimos necesarios para este ejercicio se indican a continuación:
3.Una vez instalado ejecuta XAMPP Control Panel:
4.Pulsa los botones Start, tanto para Apache como para MySQL.
5.Para verificar que el servidor web está en marcha, abre un navegador y desde la barra de direcciones accede a http://localhost. Se mostrará una página con información sobre XAMPP.
Ejercicio:Configuración de Apache
En este ejercicio veremos una visión superficial sobre la configuración del servidor web Apache.
1. Verifica que el servidor está arrancado accediendo a la dirección http://localhost. Se utiliza para referirte a tu propia dirección IP. Es equivalente a escribir http://127.0.0.1.
2. Dentro de la carpeta donde hayas instalado XAMPP (por ejemplo, C:/xampp) abre la carpeta htdocs. Dentro encontrarás todos los ficheros que publica el servidor.
3. El fichero que toma por defecto en esta carpeta es index.php (o index.html si no lo encuentra). Edita este fichero y estudia su estructura. Modifica este fichero y recarga en el navegador para observar los cambios (http://localhost).
4. Ejecuta XAMPP Control Panel y pulsa el botón Config de Apache. Selecciona Apache (httpd.conf). Se editará el fichero xampp/apache/conf/httpd.conf. Es un fichero bastante largo, aunque normalmente solo tendrás que modificar los siguientes parámetros:
Listen 80 - Indica que el servidor escucha el puerto 80. Es frecuente cambiar este valor por 8080 o 888. Para aceptar conexiones solo de este host, cambia la línea por Listen 127.0.0.1:80.
ServerAdmin postmaster@localhost - Correo electrónico del administrador del servidor.
ServerName localhost:80 - Nombre del servidor, indicando nombre de dominio y puerto. Si no se indica, trata de obtenerlo automáticamente.
DocumentRoot "C:/xampp/htdocs" - El directorio donde se encuentran los documentos servidos por el servidor.
DirectoryIndex index.php index.pl … index.html - Establece el archivo que Apache ofrece cuando se solicita un directorio sin indicar un archivo concreto. De encontrar varios se escoge el que esté antes en esta lista.
Si modificas algún valor, recuerda guardar el fichero y reinicializar Apache para que se carguen los nuevos valores.
5. Vamos a probar si el servidor web es accesible desde otros dispositivos conectados a tu red de área local. Utiliza el comando ipconfig (Windows) o ifconfig (Linux/Mac) para averiguar la dirección IP de tu ordenador.
6. Abre un navegador desde otro dispositivo y accede a la dirección que acabas de obtener. Si lo haces desde un móvil, has de acceder a través de Wi-Fi. Si tienes problemas, es posible que sea culpa del cortafuegos. En este caso tendrás que desactivarlo.
Ejercicio:Un servicio web con PHP y MySQL
En este ejercicio comenzamos creando una base de datos y luego escribiremos un par de ficheros PHP que implementarán las dos acciones del servicio web puntuaciones.
1.Ejecuta XAMPP Control Panel y asegúrate de que tanto Apache como MySQL están arrancados.
2.Pulsa el botón Admin de MySQL que encontrarás en XAMPP Control Panel. De esta forma accedemos a la herramienta de administración del servidor de bases de datos phpMyAdmin.
3.Selecciona la lengüeta SQL e introduce las siguiente instrucciones en el cuadro de texto:
CREATE DATABASE IF NOT EXISTS puntuaciones;
USE puntuaciones;
CREATE TABLE puntuaciones (
_id INTEGER PRIMARY KEY AUTO_INCREMENT,
puntos INTEGER, nombre TEXT, fecha BIGINT);
INSERT INTO puntuaciones (puntos, nombre, fecha) VALUES
(10000, 'Pedro', 0),
(20000, 'Rosa', 0);
4.Pulsa el botón OK para ejecutar estas sentencias.
5.En el marco de la izquierda, pulsa el botón verde con forma de recargar. Observa como en la lista de bases de datos aparece puntuaciones. Si pulsas en el botón + de su izquierda se mostrarán sus tablas.
6.Selecciona la tabla puntuaciones para examinar su contenido.
Como puedes observar, disponemos de diferentes herramientas para editar los valores de la tabla. Por ejemplo, podemos utiliza la lengüeta Insertar para añadir una nueva fila a la tabla.
7.Explora otras utilidades que nos ofrece phpMyAdmin para trabajar con bases de datos.
8.Dentro de la carpeta donde hayas instalado XAMPP (por ejemplo, C:/xampp), abre la carpeta htdocs. Crea dentro la carpeta puntuaciones.
9.Dentro de esta carpeta crea el fichero lista.php con el siguiente contenido:
<?php
$con = new mysqli('localhost', 'root', '', 'puntuaciones');
if ($con->connect_errno) {
echo 'Error al conectar base de datos: ', $con->connect_error;
exit();
}
$sql = 'SELECT puntos, nombre FROM puntuaciones ORDER BY fecha DESC';
if (isset($_GET['max'])) {
$sql .= ' LIMIT ?';
}
$cursor = $con->stmt_init();
if ($cursor->prepare($sql)) {
if (isset($_GET['max'])) {
$cursor->bind_param("s",$_GET['max']);
}
$cursor->execute();
$cursor->bind_result($puntos, $nombre);
while($cursor->fetch()) {
echo $puntos.' '.$nombre."\n";
}
echo "\n";
$cursor->close();
}
$con->close();
?>
El código PHP suele estar entremezclado entre el código HTML. Para diferenciarlo de este, hay que introducirlo entre los caracteres <?php y ?>. La primera sentencia establece una conexión a una base de datos situada en un servidor MySQL. Necesita cuatro parámetros: primero, la dirección IP donde está el servidor (cuando se indica localhost nos referimos a nuestra propia IP); luego, usuario y contraseña usados en la conexión (en el ejemplo, usuario root y sin contraseña; sería muy conveniente introducir otros valores en un caso real); finalmente, el nombre de la base de datos a utilizar. La conexión se guarda en el objeto $con. Observa como las variables en PHP siempre comienzan con el carácter $, además no han de declararse.
En la siguiente línea se accede a una propiedad del objeto $con para verificar si ha habido algún error. Observa como para aceder a las propiedades de un objeto en PHP se utilizan los caracteres -> en lugar del carácter . usado en Java. Luego se configura la codificación de caracteres y se inicializa la variable $sql con la consulta a realizar. Solo nos interesa puntos y nombre de la tabla puntuaciones ordenados por fecha. Para concatenar dos cadenas en PHP se utiliza el carácter punto (.).
En el siguiente if verificamos si nos han pasado el parámetro max a través de la URL. Por ejemplo:
http://localhost/puntuaciones/lista.php?max=10
En caso de que el array$_GET[ ] contenga este parámetro, añadimos a la consulta SQL una restricción en el número de parámetros devueltos.
A continuación preparamos y ejecutamos la consulta SQL que se recogerá en la variable $cursor. Utilizando el método bind_result() asociamos los dos campos indicados en la cláusula SELECT con dos variables PHP. Luego recorremos todos los elementos de $cursor y por cada uno devolvemos una línea de texto plano con los puntos, el nombre y un salto de línea. Más adelante intentaremos devolverlo en un formato XML. Terminamos cerrando el cursor y la conexión.
10. Abre un navegador web y escribe la siguiente dirección:
http://localhost/puntuaciones/lista.php?max=10
11. Es posible que el resultado se muestre en una sola línea. El navegador espera como resultado de la consulta una página HTML, y no hemos introducido en el resultados la etiqueta <br/> tras cada línea. Selecciona la opción Ver código fuente de la página para ver el resultado correctamente.
12. Crea el fichero nueva.php en la misma carpeta con el siguiente código:
<?php
$con = new mysqli('localhost', 'root', '', 'puntuaciones');
if ($con->connect_errno) {
echo 'Error al conectar base de datos: ', $con->connect_error;
exit();
}
$puntos = $_GET['puntos'];
$nombre = htmlspecialchars($_GET['nombre']);
$fecha = $_GET['fecha'];
$sql = $con->prepare('INSERT INTO puntuaciones VALUES (null,?, ?, ?)');
$sql->bind_param('isi', $puntos, $nombre, $fecha);
$sql->execute();
echo 'OK\n';
$con->close();
?>
13. Puedes comprobar su funcionamiento accediendo a la siguiente dirección:
14. Verifica que el nuevo elemento ha sido añadido. Puedes usar la URL:
http://localhost/puntuaciones/lista.php?max=10
-UtilizandoelserviciowebPHPdesdeAsteroides
En este apartado vamos a realizar un cliente del servicio web diseñado en el apartado anterior para usarlo desde Asteroides.
Ejercicio:Uso del servicio web PHP en Asteroides
En este ejercicio comenzamos creando una base de datos y luego escribiremos un par de ficheros PHP que implementarán las dos acciones del servicio web puntuaciones.
1.Abre el proyecto Asteroides.
2.Vamos a hacer el acceso a la red desde el hilo principal. Para evitar que StrictMode nos lo impida, añade el siguiente código en el método onCreatede MainActivity.java:
3.Para poder acceder a StrictMode tenemos un nivel de API mínimo de 9. Abre AndroidManifest.xml y asegúrate de que el valor de minSdkVersion sea igual o mayor que 9:
<uses-sdk android:minSdkVersion="9"
4.Como en todos los ejemplos de este tema, asegúrate de que la aplicación solicita el permiso de acceso a Internet. Añade la siguiente línea en AndroidManifest.xml:
5.Pasemos a implementar la interfaz AlmacenPuntuacionesaccediendo al servidor de servicios web que acabamos de desarrollar. Para ello crea una nueva clase en la aplicación Asteroides y copia el siguiente código:
public class AlmacenPuntuacionesSW_PHP implements AlmacenPuntuaciones {
public Vector<String> listaPuntuaciones(int cantidad) {
Vector<String> result = new Vector<String>();
try {
URL url=new URL("http://158.42.146.127/puntuaciones/lista.php"
+ "?max=20");
HttpURLConnection conexion = (HttpURLConnection) url
.openConnection();
if (conexion.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new
InputStreamReader(conexion.getInputStream()));
String linea = reader.readLine();
while (!linea.equals("")) {
result.add(linea);
linea = reader.readLine();
}
reader.close();
} else {
Log.e("Asteroides", conexion.getResponseMessage());
}
} catch (Exception e) {
Log.e("Asteroides", e.getMessage(), e);
} finally {
if (conexion!=null) conexion.disconnect();
return result;
}
}
El primer método se encarga de invocar la operación lista.php del servicio web que acabamos de implementar. Comienza definiendo la URL correspondiente al servicio web. En el código hay que reemplazar “158.42.146.127” por la dirección IP de tu ordenador. Recuerda que este programa lo ejecutarás desde el emulador o desde un teléfono real, y en ambos casos la IP será diferente de la de tu ordenador. Esto imposibilita utilizar como dirección localhost, como sí hicimos con otros clientes que ejecutábamos desde el mismo ordenador.
Una vez creada la URL se establece la conexión y mediante el método GET se manda el parámetro correspondiente.
6.Pasemos a ver el segundo método de la clase. A continuación copia el siguiente código:
public void guardarPuntuacion(int puntos, String nombre, long fecha) {
try {
URL url=new URL("http://158.42.146.127/puntuaciones/nueva.php?"
+ "puntos="+ puntos
+ "&nombre="+ URLEncoder.encode(nombre, "UTF-8")
+ "&fecha=" + fecha);
HttpURLConnection conexion = (HttpURLConnection) url
.openConnection();
if (conexion.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new
InputStreamReader(conexion.getInputStream()));
String linea = reader.readLine();
if (!linea.equals("OK")) {
Log.e("Asteroides","Error en servicio Web nueva");
}
} else {
Log.e("Asteroides", conexion.getResponseMessage());
}
} catch (Exception e) {
Log.e("Asteroides", e.getMessage(), e);
} finally {
if (conexion!=null) conexion.disconnect();
}
}
}
La estructura de este método es similar al anterior, pero ahora llamamos a la operación nueva.php. Recuerda que en caso de una llamada satisfactoria, la respuesta ha de ser OK. Consideraremos que ha habido un error si esta no es la respuesta.
7.Puedes reemplazar “158.42.146.127” por la dirección IP de tu ordenador.
8. Modifica el código correspondiente para que la nueva clase pueda ser seleccionada como almacén de las puntuaciones.
9.Verifica el funcionamiento.
-Creacióndeunserviciowebenunservidordehosting
Existen muchas empresas que ofrecen servicio de hosting, algunas incluso de forma gratuita. Por lo general, suele ser una solución más sencilla, fiable y barata que mantener nuestros propios servidores.
En este apartado aprenderemos a montar un servicio web en uno de estos servidores. Hemos elegido la empresa Hostinazo porque ofrece uno de los mejores paquetes gratuitos. Aunque en la actualidad esta empresa incrusta propaganda en su servicio gratuito, podemos encontrar otras empresas que no la incrustan. Este inconveniente podrá evitarse, dado que usaremos el hosting para nuestro servicio web y la propaganda incrustada no será vista por nuestros usuarios.
Los pasos del siguiente ejercicio se han preparado para el hosting Hostinger. No obstante, estos pasos son muy similares para otras empresas de hosting.
Ejercicio:Uso del servicio web en un servidor de hosting
En este ejercicio comenzamos creando una base de datos y luego escribiremos un par de ficheros PHP que implementarán las dos acciones del servicio web puntuaciones.
3.Una vez completado, te enviarán un correo para activar tu cuenta. Entra en tu correo y pulsa sobre el enlace que te indican. Si no recibes ningún correo, verifica la carpeta de spam.
4.Con tu usuario activado podrás crear un nuevo plan de hosting gratuito. Te pedirá que indiques si quieres usar tu propio dominio o usar un subdominio. Selecciona esta última opción e introduce el subdominio que prefieras. También te pedirá una nueva contraseña asociada a este hosting.
En este ejercicio hemos usado el subdominio jtomas.esy.es. Tendrás que seleccionar uno diferente y acordarte de reemplazarlo en el resto del ejercicio.
5.Pasados unos minutos, ya dispondrás de tu cuenta. Refresca el navegador hasta que se muestre la siguiente información:
6.Introduce en un navegador tu dominio para ver que ya funciona. Por supuesto, podrás poner tu propio contenido.
7.Pulsa el botón Administrar para gestionar tu cuenta. Dispondrás de decenas de herramientas para gestionar diferentes aspectos de tu hosting. En la sección Bases de Datos encontrarás las siguientes opciones:
8.Selecciona la opción Bases de Datos MySQL y crea una nueva base de datos. Te pedirá que completes los siguientes datos:
Apunta estos datos, los vas a necesitar más tarde. Has de saber que este hosting gratuito solo te permite una base de datos. Pulsa Create.
9.Regresa a cPanel. Para ello pulsa el nombre de la cuenta en la barra de navegación .
Selecciona la herramienta phpMyAdmin. Te mostrará una lista con tus bases de datos. Pulsa sobre el enlace Ingresar phpMyAdmin.
10.Esta herramienta de gestión de bases de datos ya la utilizamos cuando instalamos el servidor en local. Para crear la tabla puntuaciones selecciona la lengüeta SQL e introduce las siguiente instrucciones en el cuadro de texto:
CREATE TABLE puntuaciones (
_id INTEGER PRIMARY KEY AUTO_INCREMENT,
puntos INTEGER, nombre TEXT, fecha BIGINT);
INSERT INTO puntuaciones (puntos, nombre, fecha) VALUES
(10000, 'Pedro', 0),
(20000, 'Rosa', 0);
11.Pulsa el botón Continuar para ejecutar estas sentencias SQL.
12.Recarga la página y observa como en la lista de bases de datos de la izquierda aparece la tabla Puntuaciones. Si pulsas sobre esta, podrás visualizar y editar su contenido.
13.Regresa a Administrar y en la sección Archivos selecciona Administrador de Archivos 2. Esta herramienta se situará en la raíz de tu almacenamiento.