Está en la página 1de 12

Protocolos de comunicación en red

Last updated 6 months ago

Repaso de la arquitectura TCP/IP


La arquitectura TCP/IP está compuesta por una serie de capas o niveles en los
que se encuentran los protocolos que implementan las funciones necesarias
para la comunicación entre dos dispositivos en red. Esta arquitectura es
independiente del modelo teórico OSI, aunque tiene muchas similitudes (ambos
modelo se basan en capas o niveles). Se puede afirmar que el modelo OSI es
el empleado en el estudio de las redes de datos mientras que el modelo o
arquitectura TCP/IP es un modelo real empleado es las redes actuales.

En la siguiente figura se aprecian los niveles o capas de los modelos OSI y


TCP/IP.

Representación de capas o niveles OSI y TCP/IP.

A continuación, se describe cada una de las capas y los protocolos incluidos en


ellas dentro del modelo TCP/IP.

Capa de acceso a red

Ofrece la capacidad de acceder a cualquier red física, es decir, brinda los


recursos que se deben implementar para transmitir datos a través de la red
local. Por tanto, la capa de acceso a la red contiene especificaciones
relacionadas con la transmisión de datos por una red física (red local) Ethernet,
en anillo, FDDI, etc. En este nivel, dependiendo del hardware de acceso, se
define la estructura de datos conocida como trama que tendrá una vida útil
unicamente en la red local.

Representación de una trama Ethernet.

En este nivel se definen direcciones físicas o direcciones MAC de los


dispositivos. Las tramas emplearán estas direcciones para especificar origen y
destino de los datos que transportan. Las direcciones MAC están compuestas
por 6 bytes (48 bits) donde los tres primeros bytes identifican al fabricante de la
tarjeta de red. Los otros tres bytes identifican a la tarjeta.

Representación de una dirección MAC en formato hexadecimal.

Capa de Internet

La capa de Internet, también conocida como capa de red o capa IP, acepta y
transfiere paquetes para la red. Esta capa incluye el famoso protocolo de
Internet (IP), el protocolo de resolución de direcciones (ARP) y el protocolo de
mensajes de control de Internet (ICMP).

Protocolo IP

El protocolo IP y sus protocolos de enrutamiento asociados son posiblemente


la parte más significativa del conjunto TCP/IP. El protocolo IP se encarga de:

 Direcciones IP: Las convenciones de direcciones IP forman parte del protocolo


IP. Se corresponde con la identificación de equipos en la red. Las direcciones
IP cambian en función de la red en la que está presente el dispositivo. Las
direcciones IP identifican equipos en la conexión extremo a extremo.
Actualmente se emplean direcciones de versión 4 (32 bits) y versión 6 (128
bits).
 Encaminamiento: El protocolo IP determina la ruta que debe utilizar un
paquete, basándose en la dirección IP del destinatario.
 Formato de paquetes: el protocolo IP agrupa paquetes en unidades conocidas
como datagramas. Los datagramas viajaran entre el origen y destino IP dentro
de las tramas de datos.
 Fragmentación: Si un paquete es demasiado grande para su transmisión a
través del medio de red, el protocolo IP del sistema de envío divide el paquete
en fragmentos de menor tamaño. Cuando los fragmentos llegan al receptor, el
protocolo IP del sistema receptor reconstruye los fragmentos y crea el paquete
original.
El que una aplicación conozca la IP de la máquina en la que está siendo
ejecutada es fundamental para aplicaciones que requieren concexión de datos.
En el capítulo anterior se presentó un método para obtener esta identificación si
se empleaba una red Wifi. Independientemente del tipo de red al que nos
conectemos con el móvil tenemos más opciones para informar de la IP del
equipo. En este caso utilizamos la clase InetAddress y el método
getHostAddress().

El siguiente código hade uso del método anterior para obtener la IP del
smartphone:

private String getIpAddress()


{
String ip = "";
try
{
Enumeration<NetworkInterface> enumNetworkInterfaces =
NetworkInterface.getNetworkInterfaces();
while (enumNetworkInterfaces.hasMoreElements())
{
NetworkInterface networkInterface =
enumNetworkInterfaces.nextElement();
Enumeration<InetAddress> enumInetAddress =
networkInterface.getInetAddresses();
while (enumInetAddress.hasMoreElements())
{
InetAddress inetAddress =
enumInetAddress.nextElement();

if (inetAddress.isSiteLocalAddress())
{
ip += "IP: " + inetAddress.getHostAddress() +
"\n";
}

} catch (SocketException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
ip += "¡Algo fue mal! " + e.toString() + "\n";
}

return ip;
}
Protocolo ARP

El protocolo de resolución de direcciones (ARP) se encuentra conceptualmente


entre el vínculo de datos (acceso a red) y las capas de Internet. ARP ayuda al
protocolo IP a dirigir los datagramas al sistema receptor adecuado realizando la
correspondencia entre direcciones MAC (48 bits de longitud) y las direcciones
IP conocidas (32 bits de longitud).

Protocolo ICMP

El protocolo de mensajes de control de Internet (ICMP) detecta y registra las


condiciones de información y error de la red. Situaciones como la falta de
conectividad, problemas en la fragmentación o la redirección de datagrams son
detectadas e informadas al origen con mensajes ICMP.

Capa de transporte

La capa de transporte TCP/IP se encarga de identificar las aplicaciones que


desean conectarse en red. Recibe y envía datos de las aplicaciones y, en
función de la seguridad y/o fiabilidad necesaria, así como la rapidez, puede
prestar dos tipos de servicio a las aplicaciones: seguro y fiable (TCP), frente a
rápido y no fiable (UDP).

En la capa de transporte se definen los números de puerto (16 bits). En Internet


se suele asociar a cada aplicación un número de puerto concreto. En la
máquina que hace de "servidor" estos números de puerto se asocian a
aplicaciones genéricas: 80 para la web, 25 para el correo electrónico, 7 para
ECHO o 4661 para eDonkey. El cliente suele emplear puertos con una
numeración más elevada, también de 16 bits, que se incrementará cada vez
que se abre una nueva aplicación que desea comunicación en red. Estos
números podrán ser, por ejemplo: 1242, 2045, 3021, etc.

Protocolo TCP

TCP permite a las aplicaciones comunicarse entre sí como si estuvieran


conectadas físicamente. TCP envía los datos en un formato que se transmite
carácter por carácter, en lugar de transmitirse por paquetes discretos. Esta
transmisión consiste en establecer una conexión y un control de la llegada de
los datos. Cuando todo ha sido enviado exitosamente se procede al cierre de la
conexión. El protocolo de transporte denomina a su estructura de datos como
segmento.

La descarga Web o la transferencia de ficheros precisan de una conexión con


TCP.
TCP, por tanto, confirma que un paquete ha alcanzado su destino dentro de
una conexión establecida entre los hosts de envío y recepción. El protocolo
TCP se considera un protocolo fiable orientado a la conexión.

Protocolo UDP

UDP proporciona un servicio de entrega de datagramas. UDP no verifica las


conexiones entre los hosts transmisores y receptores. Dado que el protocolo
UDP elimina los procesos de establecimiento y verificación de las conexiones,
resulta ideal para las aplicaciones que envían pequeñas cantidades de datos o
cuando el volumen de datos es elevado y éste tiene que llegar en tiempo real
(aquí se prefiere la rapidez a la fiabilidad).

VoIP es un ejemplo de aplicación que utilizará UDP.

Capa de aplicación

La capa de aplicación define las aplicaciones de red y los servicios de Internet


estándar que puede utilizar un usuario. Estos servicios utilizan la capa de
transporte para enviar y recibir datos. Existen varios protocolos estandarizados
de aplicación:

 HTTP (páginas web)


 FTP (tranferencia de archivos)
 telnet (conexión a servidor)
 SSH (conexión segura a servidor)
 SMTP (correo electrónico)
 POP (correo electrónico)
 DNS (resolución de nombres de dominio)
Estructura de datos transporte en TCP/IP
Siguiendo el esquema de la encapsulación de protocolos de TCP/IP, los datos
de las aplicaciones se introducirán en segmentos TCP o en datagramas UDP
en función del requerimiento en cuanto a seguridad, fibilidad o rapidez
comentado anteriormente.

La estructura de datos de TCP se denomina segmento y posee una cabecera


de control considerablemente mayor que la cabecera del datagrama UDP que
es la denominación de la estructura de datos de UDP. En este último caso,
UDP añade a los datos de aplicación los números de puerto origen y destino.
TCP necesita más campos en su cabecera para controlar la información
enviada y, por tanto, ademas de los números de puerto tendremos números de
secuencia, número de ACK, señalización, etc.

Representación de la cabecera de TCP.

Sockets

Los sockets son un sistema de comunicación entre procesos de diferentes


máquinas en red (ordenadores, smartphones,...). Más exactamente, un socket
es un punto de comunicación por el cual un proceso puede emitir o recibir
información.

Cuando utilizamos Sockets para comunicar procesos nos basamos en la


arquitectura cliente y servidor. Así pues, estableceremos dos Sockets: uno será
la parte servidor y recibirá la transmisión del cliente y otro será la parte cliente
que recibirá la respuesta del servidor.

Dependiendo el protocolo con el que vamos a realizar la conexión, tendremos


dos tipos de Socket, los que utilizan el protocolo TCP, y los que utilizan el
protocolo UDP. Ambos sockets utilizan la dirección IP (32 bits) así como el
número de puerto (16 bits) de las máquinas cliente y servidor para poder
establecer los puntos de comunicación de los procesos (una conexión estará
formada por un par de sockets, que son los extremos de la conexión).
Sockets stream (TCP)

Los sockets stream ofrecen un servicio orientado a conexión, donde los datos
se transfieren como un flujo continuo, sin encuadrarlos en registros o bloques.
Este tipo de socket se basa en el protocolo TCP que, tal y como se ha
comentado antes, es un protocolo orientado a conexión. Esto implica que antes
de transmitir información hay que establecer una conexión entre los dos
sockets. Mientras uno de los sockets atiende peticiones de conexión (servidor),
el otro solicita la conexión (cliente). Una vez que los dos sockets está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.

Sockets datagram (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 TCP, pero tiene el inconveniente que no se garantiza la fiabilidad.
Además, los datos se envían y reciben en datagramas (paquetes de
información) de tamaño no limitado a un valor concreto. La entrega de un
datagrama no está garantizada: estos pueden ser duplicados, perdidos o llegar
en un orden diferente al que se envió.

La gran ventaja de este tipo de sockets es que apenas introduce sobrecarga


sobre la información transmitida. Además, los retrasos introducidos son
mínimos, lo que los hace especialmente interesantes para aplicaciones en
tiempo real, como la transmisión de audio y vídeo sobre Internet. Sin embargo,
presenta muchos inconvenientes al 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.

Programación de Sockets

El modelo comunicación de los sockets es el siguiente:

 El servidor establece un puerto y espera durante un cierto tiempo (timeout) a


que el cliente establezca la conexión. Cuando el cliente solicite una conexión,
el servidor abrirá esta conexión socket con el método accept().
 El cliente establece una conexión con la máquina host a través del puerto que
se designe el parámetro respectivo.
 El cliente y el servidor se comunican con manejadores InputStream y
OutputStream.
 Se procede al cierre de la conexión.
En la siguiente figura se representa este esquema de comunicación de sockets
entre cliente y servidor.

Conexión de sockets y comunicación entre cliente y servidor.

Los permisos en el manifest para las conexiones cliente / servidor pueden ser
únicamente:

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

Como recomendación del alumno Rafael Espí Botella, es importante tener en


cuenta la targetApi que se declara en el manifest, pues en los móviles con
Android 6.0 se deben gestionar los permisos de la app de forma explícita. Los
que poseen la api 22 o anterior deberían funcionar con normalidad. Por lo
tanto, lo más práctico es dejar la targetApi 22 y compilar con 22 ó 23. Más
información en:
http://developer.android.com/intl/es/training/permissions/declaring.html

Creación y apertura de sockets

A la hora de programar aplicaciones cliente / servidor, es importante tener en


cuenta el rol de cada máquina, algo que se tendrá en cuenta en la
programación a desarrollar. En la parte de cliente:

Socket miCliente;
miCliente = new Socket(IPservidor, numeroPuerto);

Donde IPservidor será la IP del equipo con el que intentamos una conexión y
numeroPuerto es el puerto (un número) sobre el cual nos queremos conectar.
Cuando se selecciona un número de puerto, se debe tener en cuenta que los
puertos en el rango 0-1023 están reservados para usuarios con muchos
privilegios (superusuarios o root). Estos puertos son los que utilizan los
servicios estándar del sistema como email, ftp o http. Para las aplicaciones que
se desarrollen, es importante asegurarse la selección de un puerto por encima
del 1023.

En el ejemplo anterior no se usan excepciones; sin embargo, es una gran idea


la captura de excepciones cuando se está trabajando con sockets. El mismo
ejemplo quedaría como:

Socket miCliente;
try {
miCliente = new Socket(IPservidor, numeroPuerto);
}
catch(IOException e)
{
System.out.println(e);
}

Si estamos programando un Servidor, la forma de apertura del socket es la que


muestra elsiguiente ejemplo:

Socket miServicio;
try {
miServicio = new ServerSocket(numeroPuerto);
}
catch(IOException e)
{
System.out.println(e);
}

A la hora de la implementación de un servidor también necesitamos crear un


objeto socket desde el ServerSocket para que esté atento a las conexiones que
le puedan realizar clientes potencialesy poder aceptar esas conexiones:

Socket socketServicio = null;


try {
socketServicio = miServicio.accept();
}
catch(IOException e)
{
System.out.println(e);
}

Creación de streams

Antes de continuar, aclarar que 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 al socket. Lo cual nos permitirá
obtener las variables, entrada y salida mediante las que podremos recibir y
transmitir información, que es el siguiente paso que a continuación se detalla.

Streams de entrada

En la parte Cliente de la aplicación, se puede utilizar la clase DataInputStream


para crear unstream de entrada que esté listo a recibir todas las respuestas
que el servidor le envíe.

DataInputStream entrada;
try {
entrada = new DataInputStream(miCliente.getInputStream());
}
catch(IOException e)
{
System.out.println(e);
}

La clase DataInputStream permite la lectura de líneas de texto y tipos de datos


primitivos de Java de un modo altamente portable; dispone de métodos para
leer todos esos tipos como: read(), readChar(), readInt(), readDouble() y
readLine(). Deberemos utilizar la función que creamos necesaria dependiendo
del tipo de dato que esperemos recibir del servidor.

En el lado del Servidor, también usaremos DataInputStream, pero en este caso


para recibir las entradas que se produzcan de los clientes que se hayan
conectado:

DataInputStream entrada;
try {
entrada = new DataInputStream(socketServicio.getInputStream());
}
catch(IOException e)
{
System.out.println(e);
}

Streams de Salida

En la parte del Cliente podemos crear un stream de salida para enviar


información al socket del servidor utilizando las clases PrintStream, PrintWriter
o DataOutputStream:

PrintStream salida;
try {
salida = new PrintStream(miCliente.getOutputStream());
}
catch(IOException e)
{
System.out.println(e);
}
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream())),
true);

out.println(mensaje);

catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
DataOutputStream salida;
try {
salida = new DataOutputStream(miCliente.getOutputStream());
}
catch(IOException e)
{
System.out.println(e);
}

La clase DataOutputStream permite escribir cualquiera de los tipos primitivos


de Java, muchosde sus métodos escriben un tipo de dato primitivo en el stream
de salida. De todos esos métodos, el más útil quizás sea writeBytes().

En el lado del Servidor, podemos utilizar la clase PrintStream para enviar


información al cliente:

PrintStream salida;
try {
salida = new PrintStream(socketServicio.getOutputStream());
}
catch(IOException e)
{
System.out.println(e);
}

Pero también podemos utilizar la clase DataOutputStream como en el caso de


envío de información desde el cliente.

Cierre de sockets

Siempre deberemos cerrar los canales de entrada y salida que se hayan


abierto durante la ejecución de la aplicación. En la parte del cliente:

try
{
salida.close();
entrada.close();
miCliente.close();
}
catch (IOException e)
{
System.out.println(e);
}

Y en la parte del servidor:

try
{
salida.close();
entrada.close();
socketServicio.close();
miServicio.close();
}
catch (IOException e)
{
System.out.println(e);
}

Programación en Android de aplicación básica


cliente / servidor

Conexión entre cliente y servidor

A continuación se detalla parte del código para lograr una conexión exitosa
entre dos terminales smartphone. Uno de ellos será el cliente y el otro el
servidor.

También podría gustarte