Protocols

Els protocols d'Internet tenen més d'un model. El model OSI ens dona una organització per capes:

  1. Física: transmissió i recepció de bits sobre el mitjà físic.
  2. Enllaç: transmissió fiable de trames entre dos nodes. PPP.
  3. Xarxa: transmissió de paquets sobre una xarxa multi-node, amb adreçament, encaminament i control de tràfic. IP.
  4. Transport: transmissió de segments de dades entre punts d'una xarxa. TCP, UDP.
  5. Sessió: gestió de sessions.
  6. Presentació: traducció dels protocols cap a una aplicació. MIME, SSL.
  7. Aplicació: APIs d'alt nivell. HTTP, Websockets.

Com a programadors, podem involucrar-nos a diferents nivells del model:

  • Construint protocols basats en TCP, UDP mitjançant Java Sockets (nivell 4 i mig).
  • Accedint a aplicacions web HTTP (nivell 7), implementades als ports 80 o 443 (segur).

Els protocols de nivell 4 i mig estàndards es poden veure a aquesta llista.

IP versió 4

Les adreces IPv4 utilitzen 32 bits, i tenen rangs assignats a diferents propòsits:

  • Classe A: de 1.x.x.x a 127.x.x.x (subnet mask 255.0.0.0). El rang 127.x.x.x està reservat per al loopback.
  • Classe B: de 128.0.x.x a 191.255.x.x (subnet mask 255.255.0.0).
  • Classe C: de 192.0.0.x a 223.255.255.x (subnet mask 255.255.255.0).
  • Classe D: de 224.0.0.0 a 239.255.255.255. Reservades per a multicasting.
  • Classe E: de 240.0.0.0 a 255.255.255.254. Reservades per a recerca.

D'aquestes, es consideren IP privades:

  • 10.0.0.0 a 10.255.255.255
  • 172.16.0.0 a 172.31.255.255
  • 192.168.0.0 a 192.168.255.255

Aquests són els tipus d'encaminaments disponibles:

Unicast: adreces de classe A, B i C. Transport TCP i UDP.

Broadcast: Adreça del host (sufix) amb tot 1's. Transport UDP. No travessen els encaminadors, i les reben totes les màquines. No disponible a IPv6.

Multicast: adreces de classe D. Transport UDP. Una màquina ha d'escoltar per rebre la comunicació. Disponible a xarxes locals.

IP versió 6

IPv6 és la versió 6 del Protocol d'Internet (IP), i està dissenyat per substituir l'actual IPv4 a internet, però encara no està suportat completament. Les adreces utilitzen 128 bits, que es mostren en grups de 4 dígits hexa.

xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx

IPv6 no té classes. Quan es fa subxarxes, la notació /NN indica la longitud del prefix de xarxa. Per exemple:

2001:0db8:1234::/64

La notació :: indica un o més grups de valor 0. Només hi pot haver un :: en una adreça. El loopback es representa com ::1/128 o, simplement, ::1.

Els tipus d'encaminament d'IPv6 són unicast, multicast i anycast (el tipus broadcast no existeix a IPv6). Anycast permet enviar un missatge a un group on només contesta un, el més proper.

Disseny de protocols

Introducció al disseny

Els protocols regeixen la comunicació entre un client i un servidor. Un protocol ben dissenyat garanteix fiabilitat, claredat i extensibilitat. El disseny del protocol implica definir:

  • Format del missatge: Estructura de peticions i respostes.
  • Flux de treball: seqüència d'interaccions entre client i servidor.
  • Conversió de flux de bytes: codificació i descodificació de missatges per a la transmissió.

En la comunicació de xarxa, les dades es transmeten com un flux de bytes. Sense una estructura definida, és impossible determinar on acaba un missatge i on comença el següent, o com interpretar el contingut d'un missatge. Un protocol preveu:

  • Límits: marcadors per identificar l'inici i el final dels missatges.
  • Regles de descodificació: instruccions per interpretar el flux de bytes en brut en dades significatives.
  • Coherència: un format estandarditzat per a la comunicació.

Per exemple, un client que envia una sol·licitud d'inici de sessió i un servidor que respon ha d'acordar com codificar el nom d'usuari, la contrasenya i l'estat de resposta en un flux de bytes, així com com tornar-lo a descodificar.

Objectius:

  • Estandarditzar la comunicació entre client i servidor.
  • Minimitzar errors i ambigüitats.
  • Admet camps de dades flexibles i de longitud variable.

Disseny d'alt nivell

Abans de dissenyar el protocol, identificar:

  • El servei que s'ha d'implementar (p. ex., transferència de fitxers, autenticació o xat).
  • Tipus de peticions i respostes necessàries.

A continuació, descriure la interacció típica:

  1. Configuració de la connexió: com el client i el servidor estableixen una connexió.
  2. Intercanvi petició-resposta: defineix la seqüència de missatges.
  3. Desactivació de la connexió: defineix com acaba la connexió.

Un missatge del protocol pot tenir:

  • Capçalera, metadades que defineixen el tipus de missatge i què esperem.
  • Càrrega útil o payload, amb el contingut real del missatge. Poden contenir camps de mida fixa o mida variable (amb prefixos de longitud).

Exemple de missatge:

CampDescripció
Longitud de la capçaleraMida fixa, indica la mida de la capçalera (en bytes).
Tipus de missatgeEspecifica el tipus de missatge (p. ex., sol·licitud, resposta).
Longitud de càrrega útilMida de la càrrega útil en bytes.
Càrrega útilCamp de mida variable que conté les dades reals.

Exemple de capçalera:

  • Longitud de la capçalera: 2 bytes
  • Tipus de missatge: 1 byte (p. ex., 0x01 per a la sol·licitud, 0x02 per a la resposta)
  • Longitud de càrrega útil: 4 bytes (sencer big-endian)

Exemple de payload per a inici de sessió:

CampTipusLlargada
Longitud del nom d'usuariSencer2 bytes
Nom d'usuariCadenaVariable
Longitud de la contrasenyaSencer2 bytes
ContrasenyaCadenaVariable

El procés de codificació i decodificació transforma dades de l'àmbit de la programació en fluxos de dades en fluxos de bytes, i de nou a dades. Aquest procés s'anomena també serialització i deserialització, i hauria de ser independent del llenguatge de programació, el sistema operatiu o altres condicionants.

Disseny de protocols per a TCP i UDP

En implementar un protocol, és essencial entendre les característiques específiques del protocol de transport subjacent.

TCP (Protocol de control de transmissió)

TCP proporciona un lliurament fiable, ordenat i verificat d'errors de fluxos de dades.

Característiques clau

  • Fiabilitat: TCP assegura que tots els missatges s'entreguen sense pèrdua.
  • Preservació de l'ordre: les dades arriben al receptor en el mateix ordre en què es van enviar.
  • Orientat al flux: els missatges es transmeten com un flux continu de bytes, que requereixen mecanismes addicionals per definir els límits del missatge.

Consideracions sobre l'estructura del missatge

  • Límits explícits: utilitzeu prefixos de longitud o delimitadors per separar els missatges dins del flux.
  • Reassemblatge de fragments: cal entendre que els missatges han d'enviar-se en unitats de transmissió, i que internament, l'API de Java s'encarrega de l'assemblatge i reassemblatge quan cal dividir-los.
  • Amb estat: feu un seguiment de l'estat de la connexió per gestionar els reintents o els temps d'espera de les dades no reconegudes.

UDP (Protocol de datagrama d'usuari)

UDP és un protocol sense connexió que ofereix una comunicació més ràpida però menys fiable.

Característiques clau

  • Sense fiabilitat: es poden perdre missatges o lliurar-se fora de servei sense cap retransmissió automàtica.
  • Sense connexió: no cal establir una connexió abans d'enviar dades, per la qual cosa és més ràpid que TCP.
  • Sense preservació de l'ordre: UDP no garanteix que els missatges arribin en el mateix ordre en què es van enviar.
  • Baixa sobrecàrrega: les capçaleres UDP són més petites en comparació amb les capçaleres TCP, la qual cosa la fa més eficient pel que fa a l'ús de l'ample de banda.

Consideracions sobre l'estructura del missatge:

  • Sense límits incorporats: com que UDP està orientat a missatges, cada paquet UDP és un sol missatge. Si necessiteu enviar diversos missatges en un paquet, heu de definir límits a nivell d'aplicació (p. ex., utilitzant delimitadors o prefixos de longitud).
  • Límits de mida dels missatges: els paquets UDP solen tenir una mida limitada (normalment uns 65.535 bytes), però pot ser que es necessiti fragmentació a nivell d'aplicació per a missatges més grans.
  • Sense estat: cada paquet UDP és independent, de manera que no cal mantenir els estats de connexió entre l'emissor i el receptor.

Escollir entre TCP i UDP

Quan es dissenya un protocol, l'elecció entre TCP i UDP depèn de diversos factors:

  • Requisits de fiabilitat: si s'ha de garantir l'entrega de missatges (per exemple, transaccions financeres, transferències de fitxers), TCP és la millor opció a causa de les seves capacitats de verificació d'errors i retransmissió.
  • Necessitats de rendiment: si una latència baixa és crítica i es pot tolerar la pèrdua ocasional de missatges (per exemple, la transmissió de veu o de vídeo en temps real), UDP pot ser una millor opció a causa de la seva menor sobrecàrrega i de la seva entrega més ràpida.
  • Mida i freqüència del missatge: per a protocols amb missatges grans i poc freqüents (p. ex., transferència de fitxers), el disseny orientat al flux de TCP funciona bé. Per a missatges més petits i freqüents (per exemple, consultes DNS), la senzillesa i l'eficiència d'UDP són avantatjoses.

Protocol HTTP

HTTP és un protocol de nivell aplicació per a sistemes col·laboratius i distribuïts. És el component principal de la web, gràcies a l'ús de documents d'hipertext. HTTP/1.1, la versió actual, està implementat mitjançant TCP al transport. La versió 2 ja està estandaritzada, i la 3 funcionarà sobre UDP.

La versió segura d'HTTP es diu HTTPS, o també HTTP sobre TLS, el protocol criptogràfic per a la transmissió segura.

Sessió

Una sessió és una seqüència de peticions/respostes. Comença mitjançant l'establiment d'una connexió TCP a un port d'un servidor (habitualment 80). El servidor contesta habitualment amb un codi, del tipus "HTTP/1.1 200 OK", i amb un cos, que normalment conté el recurs demanat.

HTTP es un protocol sense estat, tot i que algunes aplicacions utilitzen mecanismes per emmagatzemar informació. Per exemple, les cookies.

Missatges

Una petició conté, habitualment:

  • Una línia de petició, amb un mètode. Exemple: GET /images/logo.png HTTP/1.1
  • Camps de la capçalera de la petició. Exemple: Accept-Language: ca
  • Una línia buida.
  • Un cos opcional. Exemple: per fer un POST.

Mètodes de petició:

  • GET: el mètode habitual per obtenir un recurs. No té cos.
  • POST: el mètode utilitzat per enviar un cos al servidor. S'utilitza als formularis.
  • PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH son altres mètodes utilitzats.

Una resposta conté:

  • Una línia d'estat. Exemple: HTTP/1.1 200 OK.
  • Camps de la capçalera de la resposta. Exemple: Content-Type: text/html
  • Una línia buida.
  • Un cos opcional. Exemple: per a un GET, el contingut requerit.

Els codis d'estat poden ser del tipus:

  • Informació (1XX).
  • Èxit (2XX). Exemple: 200 OK.
  • Redirecció (3XX). Exemple: 301 Moved Permanently.
  • Error de client (4XX). Exemple: 404 Not Found.
  • Error de servidor (5XX). Exemple: 500 Internal Server Error.

Podem utilitzar el programa telnet per conectar-nos a un servidor web HTTP i enviar una comanda GET.

$ telnet maripili.es 80 Trying 217.160.0.165... Connected to maripili.es. Escape character is '^]'. GET / HTTP/1.0 Host: maripili.es

Això provoca la resposta del servidor:

HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 Connection: close Date: Wed, 16 Feb 2022 08:12:31 GMT Server: Apache <!DOCTYPE html> <html lang="es"> ... </html> Connection closed by foreign host.

Eines

Tenim tres eines per depurar protocols HTTP: netcat, curl i l'inspector dels navegadors.

Netcat permet connectar-se a un port i fer una conversa, utilitzant les canonades. Si s'indica -u utilitza UDP, si no, TCP. Per exemple, per a accedir al servei echo de la nostra màquina:

$ nc localhost 7

CURL permet obtenir la resposta d'una URL a la xarxa.

$ curl -I http://maripili.es (GET, veure headers) HTTP/1.1 200 OK Date: Fri, 05 Apr 2019 05:03:23 GMT Server: Apache X-Logged-In: False P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM" Cache-Control: no-cache Pragma: no-cache Set-Cookie: 4af180c8954a0d5a1965b5b1b23ccbc5=pc1jg8f5kqigd71iec5a5lm7k5; path=/ X-Powered-By: PleskLin Content-Type: text/html; charset=utf-8 $ curl http://maripili.es (GET, contingut de la pàgina web) $ curl -v http://maripili.es (GET, headers i contingut) $ curl -d "key1=val1&key2=val2" http://maripili.es/contacto/ (POST)

Implementació a Java

URL i HttpURLConnection

La classe URL fa referència a un recurs a la WWW. Un recurs genèric pot tenir la següent forma.

Veiem un exemple per al protocol HTTP:

https://www.example.com/test?key1=value1&key2=value2

En aquest cas, tenim que:

  • l'esquema és https
  • el host és www.example.com
  • el port és 80, però no s'indica, ja que és el valor per defecte al protocol HTTP
  • el camí (path) és test
  • la query és key1=value1&key2=value2

A Java es pot construir una URL amb:

URL url = new URL(String spec)

Un cop fet això, podem accedir a cada part de l'URL amb els mètodes getHost(), getPath(), getPort(), getProtocol(), getQuery(), etc.

Els dos mètodes més importants per interactuar amb l'URL són:

  • URLConnection openConnection(): retorna una connexió al recurs remot.
  • InputStream openStream(): retorna un InputStream per a llegir el recurs remot.

La classe URLConnection és abstracta, i si hem accedir a un recurs HTTP llavors l'objecte serà una instància de HttpURLConnection.

openStream

Per llegir una pàgina web que es trobi a l'URL d'una cadena anomenada urlText, podem fer:

URL url = new URL(urlText); BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));

Per llegir un arxiu:

BufferedInputStream in = new BufferedInputStream(new URL(urlText).openStream());

openConnection

Amb openConnection podem accedir als mètodes del protocol HTTP i els codis d'estat que es retornen o el tipus de contingut.

Aquest és un mètode GET:

URL url = new URL(urlText); HttpURLConnection httpConn = ((HttpURLConnection) url.openConnection()); httpConn.setRequestMethod("GET"); // opcional: GET és el mètode per defecte int responseCode = httpConn.getResponseCode(); String contentType = httpConn.getContentType(); BufferedReader in = new BufferedReader(new InputStreamReader(httpConn.getInputStream())); // falta llegir in: resposta del servidor

Aquest és un mètode POST:

URL url = new URL(urlText); HttpURLConnection httpConn = ((HttpURLConnection) url.openConnection()); httpConn.setRequestMethod("POST"); httpConn.setDoOutput(true); OutputStreamWriter out = new OutputStreamWriter(httpConn.getOutputStream()); out.write("propietat1=valor1&propietat2=valor2"); // valors dels paràmetres del POST out.close(); int responseCode = httpConn.getResponseCode(); String contentType = httpConn.getContentType(); BufferedReader in = new BufferedReader(new InputStreamReader(httpConn.getInputStream())); // falta llegir in: resposta del servidor