Protocols
Els protocols d'Internet tenen més d'un model. El model OSI ens dona una organització per capes:
- Física: transmissió i recepció de bits sobre el mitjà físic.
- Enllaç: transmissió fiable de trames entre dos nodes. PPP.
- Xarxa: transmissió de paquets sobre una xarxa multi-node, amb adreçament, encaminament i control de tràfic. IP.
- Transport: transmissió de segments de dades entre punts d'una xarxa. TCP, UDP.
- Sessió: gestió de sessions.
- Presentació: traducció dels protocols cap a una aplicació. MIME, SSL.
- 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:
- Configuració de la connexió: com el client i el servidor estableixen una connexió.
- Intercanvi petició-resposta: defineix la seqüència de missatges.
- 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:
Camp | Descripció |
---|---|
Longitud de la capçalera | Mida fixa, indica la mida de la capçalera (en bytes). |
Tipus de missatge | Especifica el tipus de missatge (p. ex., sol·licitud, resposta). |
Longitud de càrrega útil | Mida de la càrrega útil en bytes. |
Càrrega útil | Camp 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ó:
Camp | Tipus | Llargada |
---|---|---|
Longitud del nom d'usuari | Sencer | 2 bytes |
Nom d'usuari | Cadena | Variable |
Longitud de la contrasenya | Sencer | 2 bytes |
Contrasenya | Cadena | Variable |
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