Continuem pujant la pila. La capa d'Internet (04-03) movia paquets entre màquines; la capa de transport connecta processos entre si: el navegador de la Marta amb el servidor web de la intranet, el client de correu d'en Jon amb el servidor SMTP, cadascun identificat per un port. Els dos llogaters d'aquesta capa — TCP i UDP — ja els vas disseccionar a 02-04 (handshake, números de seqüència, finestra, datagrames), així que aquí no en repetirem la mecànica interna. El que aporta aquesta lliçó és la capa vista des del teclat: com un programa fa servir TCP a través de sockets (amb pseudocodi línia a línia), com es llegeixen els estats de connexió amb ss i netstat, què significa operativament cada estat, i què passa quan dos processos es barallen pel mateix port — el famós "address already in use" que un dia aturarà la intranet de Meridiano. És important perquè, per a un desenvolupador o un administrador, la capa de transport no és teoria: és la interfície amb què el seu codi toca la xarxa cada dia.

Contingut

  1. Què viu a la capa de transport
  2. TCP i UDP: dos serveis, un repàs en una taula
  3. Sockets: la capa vista pel programador
  4. Un servidor TCP mínim, línia a línia
  5. Un client TCP mínim, línia a línia
  6. Estats de connexió a la pràctica: ss i netstat
  7. Ports en ús i conflictes: "address already in use"
  8. TCP vs UDP com a decisió d'arquitectura

Què viu a la capa de transport

La capa de transport de TCP/IP correspon a la capa 4 d'OSI (03-05) i té dos habitants principals:

  • TCP (Transmission Control Protocol): servei orientat a connexió i fiable — estableix una sessió amb el three-way handshake, numera els bytes, confirma amb ACK, retransmet allò perdut i regula el ritme amb la finestra (tot plegat vist a 02-04).
  • UDP (User Datagram Protocol): servei sense connexió i sense garanties — envia datagrames solts i se'n desentén. Ràpid, lleuger, i honest sobre allò que no promet.

Tots dos comparteixen l'aportació clau de la capa: el multiplexatge per ports. IP lliura el paquet a la màquina 192.168.10.10; el número de port de destinació decideix a quin procés d'aquella màquina va el contingut. La parella completa que identifica una conversa és el socket pair: (IP origen, port origen, IP destinació, port destinació) — i en TCP, a més, el protocol. Per això el servidor de la intranet pot atendre la Marta i l'Ana alhora al mateix port 443: les dues converses difereixen en la IP i el port d'origen.

                 CAPA DE TRANSPORT a 192.168.10.10
        +---------------------------------------------------+
paquet  |  port 443  -> procés nginx (intranet HTTPS)        |
IP  --> |  port 22   -> procés sshd  (administració)         |
        |  port 3306 -> procés mysqld (base de dades)        |
        +---------------------------------------------------+
          una màquina, una IP, molts processos: els ports
          són el "número de pis" dins de l'edifici

TCP i UDP: dos serveis, un repàs en una taula

Sense repetir la mecànica de 02-04, fixem el contrast com a contracte de servei que la capa ofereix a les aplicacions:

Propietat TCP UDP
Connexió prèvia Sí (handshake de 3 passos) No: s'envia i prou
Fiabilitat Garantida (ACK + retransmissió) Cap: allò que es perd, perdut queda
Ordre d'arribada Garantit (números de seqüència) No garantit
Unitat que veu l'app Flux de bytes continu Datagrames amb fronteres
Control de flux/congestió Sí (finestra) No
Cost d'arrencada 1 RTT extra pel handshake Zero
Capçalera 20+ bytes 8 bytes
Segment/PDU Segment TCP Datagrama UDP

Un matís que a 02-04 va quedar implícit i que ara, amb la mirada de programador, importa molt: TCP lliura un flux, no missatges. Si el client fa dos send() de 100 bytes, el servidor els pot rebre en un sol recv() de 200, o en tres trossos de 80+80+40. Les fronteres les posa l'aplicació (per exemple, HTTP amb el seu Content-Length). En UDP, en canvi, cada datagrama arriba sencer o no arriba: un send = un recv.

Sockets: la capa vista pel programador

Un socket és l'objecte que el sistema operatiu ofereix als programes per fer servir la capa de transport. Recorda de 04-01 que la pila TCP/IP viu dins del kernel: el teu programa no construeix segments ni calcula ACK; demana al SO "dona'm un socket TCP", hi escriu bytes, i el kernel fa tota la resta (segmentar, numerar, retransmetre, i passar-ho a la capa d'Internet).

L'API de sockets (nascuda a l'Unix BSD dels anys 80 i calcada des d'aleshores en C, Python, Java, Go, JavaScript...) gira al voltant d'un grapat d'operacions:

Operació Qui la fa servir Què fa
socket() tots dos Crear el socket (triar TCP o UDP)
bind() servidor Reservar una IP local i un port
listen() servidor Declarar-se disposat a acceptar connexions
accept() servidor Esperar i acceptar una connexió entrant
connect() client Iniciar el handshake cap a IP:port remots
send() / recv() tots dos Escriure/llegir bytes del flux
close() tots dos Acabar la connexió (dispara el tancament TCP)

L'asimetria és la del món real: el servidor espera en un port conegut; el client truca. Vegem-ho en pseudocodi.

Un servidor TCP mínim, línia a línia

Així arrenca, en essència, el servidor de la intranet de Meridiano (el real és nginx + una API, però l'esquelet de sockets és aquest):

1  s = socket(TCP)                # demana al kernel un socket TCP
2  bind(s, 192.168.10.10, 8080)   # reserva la IP local i el port 8080
3  listen(s)                      # "estic obert al públic"
4  bucle infinit:
5      c = accept(s)              # es BLOQUEJA fins que arribi una connexió
6      peticio = recv(c)          # llegeix bytes del flux (p. ex. una petició HTTP)
7      send(c, resposta)          # escriu la resposta al flux
8      close(c)                   # tanca AQUESTA conversa (no el socket d'escolta)

Línia a línia, amb allò que passa per sota:

  • Línia 1socket(TCP): el kernel crea l'estructura interna i torna un identificador (en Unix, un descriptor de fitxer: per al programa, un socket es llegeix i s'escriu gairebé com un fitxer).
  • Línia 2bind(...): reserva el port 8080 a la IP 192.168.10.10. És l'únic punt on pot saltar l'error "address already in use" (ho veurem a l'apartat 7). Molts servidors fan bind a 0.0.0.0 ("totes les meves IP") en lloc d'una de concreta.
  • Línia 3listen(s): a partir d'aquí el kernel completa els handshakes tot sol. Quan el SYN de la Marta arribi (02-04), el kernel respon SYN-ACK i encua la connexió acabada, sense que el programa faci res. El socket passa a l'estat LISTEN.
  • Línia 5accept(s): treu d'aquesta cua una connexió ja establerta i torna un socket nou (c) exclusiu per a aquella conversa. Detall crucial: el socket d'escolta s continua viu i acceptant; per això el servidor atén molts clients amb un sol port. Si no hi ha connexions pendents, accept es bloqueja (el procés dorm fins que arribi algú).
  • Línies 6-7recv/send: llegir i escriure al flux. Recorda: flux de bytes, no missatges; recv pot tornar "mitja petició" i l'aplicació ha de saber recompondre-la.
  • Línia 8close(c): inicia el tancament ordenat de TCP (intercanvi de FIN/ACK, vist conceptualment a 02-04). Aquest tancament és l'origen de l'estat TIME_WAIT que de seguida veuràs a ss.

Els servidors reals hi afegeixen concurrència (un fil o tasca per connexió acceptada) i bucles de lectura, però l'esquelet — socket → bind → listen → accept → recv/send → close — és idèntic en qualsevol llenguatge.

Un client TCP mínim, línia a línia

I així es veu el costat de la Marta quan el seu navegador (o un script seu) demana alguna cosa a la intranet:

1  c = socket(TCP)                    # socket TCP, com al servidor
2  connect(c, 192.168.10.10, 8080)    # dispara el three-way handshake
3  send(c, "GET /api/proyectos ...")  # escriu la petició al flux
4  resposta = recv(c)                 # llegeix la resposta
5  close(c)                           # tancament ordenat
  • Línia 1 — igual que al servidor: el socket és la mateixa peça a tots dos costats.
  • Línia 2connect(...): aquí passa el handshake complet de 02-04 (SYN → SYN-ACK → ACK). Quan connect retorna sense error, la connexió està ESTABLISHED a tots dos extrems. Fixa't en allò que el client no va fer: cap bind. El kernel li assigna tot sol un port efímer d'origen (típicament entre 32768 i 60999 a Linux) — per això a les captures els clients sempre parlen "des de" ports alts i estranys.
  • Línies 3-5 — simètriques al servidor: escriure, llegir, tancar.

Una fallada a connect és diagnòstic pur, i ja la coneixes de 03-05: rebuig immediat (RST) = la màquina és viva però ningú no escolta en aquell port; timeout = la màquina o la xarxa no responen. La capa de transport et parla; cal saber escoltar-la.

Estats de connexió a la pràctica: ss i netstat

Tota connexió TCP viu en una màquina d'estats (el handshake i el tancament són transicions dins seu). No cal memoritzar-la sencera: en el dia a dia, tres estats expliquen el 95 % del que veuràs. L'eina moderna a Linux és ss (substituta de netstat, que continua existint i és el que hi ha a Windows).

Al servidor de la intranet, un dimarts qualsevol:

$ ss -tn
State       Recv-Q  Send-Q   Local Address:Port     Peer Address:Port
ESTAB       0       0        192.168.10.10:443      192.168.10.21:52814
ESTAB       0       0        192.168.10.10:443      192.168.10.35:49102
TIME-WAIT   0       0        192.168.10.10:443      192.168.20.7:51230

$ ss -tln
State       Recv-Q  Send-Q   Local Address:Port     Peer Address:Port
LISTEN      0       511      0.0.0.0:443            0.0.0.0:*
LISTEN      0       128      0.0.0.0:22             0.0.0.0:*

Desglossament de les opcions: -t = només TCP, -n = números sense resoldre (no tradueix 443 a "https" ni IP a noms — més ràpid i sense ambigüitat), -l = només sockets a l'escolta. A Windows, netstat -ano dona la vista equivalent (la -o hi afegeix el PID del procés).

Els tres estats clau, llegits operativament:

Estat Què significa Lectura operativa
LISTEN Socket de servidor esperant connexions (després de listen()) "El servei està arrencat i acceptant". Si esperaves un LISTEN al 443 i no hi és, el problema és el procés, no la xarxa
ESTABLISHED Handshake completat; flux obert en tots dos sentits Una conversa viva. La primera línia de dalt és la Marta (.10.21) navegant per la intranet ara mateix
TIME_WAIT La connexió ja s'ha tancat; l'extrem que va tancar primer reté el socket pair uns 60 s Normal i sa: evita que segments ressagats de la connexió vella es colin en una de nova amb el mateix socket pair. Veure molts TIME_WAIT en un servidor amb trànsit és rutina, no fuita

La tercera línia de l'exemple explica una història completa: en Jon (192.168.20.7, Bilbao) va consultar la intranet a través de la VPN, va acabar, i la seva connexió és en TIME_WAIT esperant el seu minut de quarantena abans de desaparèixer.

Altres estats que veuràs de passada: SYN-SENT (client esperant el SYN-ACK; si s'acumulen, la destinació no respon), CLOSE-WAIT (l'altre costat va tancar i la teva aplicació encara no ha cridat close() — si s'acumulen durant hores, sospita d'un bug a l'aplicació: sockets sense tancar).

Ports en ús i conflictes: "address already in use"

L'incident clàssic a Meridiano: l'Ana desplega una versió nova de l'API de la intranet i l'arrencada mor amb:

Error: bind failed - Address already in use (EADDRINUSE)

Traducció exacta: el bind() de la línia 2 del servidor va demanar un port que un altre socket ja té reservat. Les dues causes habituals, de més a menys freqüent:

  1. El procés vell continua viu. El desplegament no va matar la versió anterior, que conserva el seu LISTEN al port. Diagnòstic en una línia:

    $ ss -tlnp | grep :8080
    LISTEN  0  128  0.0.0.0:8080  0.0.0.0:*  users:(("python3",pid=1147,fd=3))
    

    L'opció -p (requereix root) delata l'ocupant: procés python3, PID 1147. Solució: aturar aquell procés ordenadament (el gestor de serveis — systemctl restart — existeix precisament per no arribar aquí). A Windows: netstat -ano | findstr :8080 i buscar el PID a l'Administrador de tareas.

  2. Connexions residuals del procés anterior (TIME_WAIT amb aquell port com a origen local en certs escenaris de reinici ràpid). Per això els servidors ben escrits activen l'opció de socket SO_REUSEADDR abans del bind: diu al kernel "permet-me reutilitzar el port encara que quedin restes en TIME_WAIT". És la primera línia que un desenvolupador afegeix a un servidor després de patir això una vegada.

Regla operativa: "address already in use" no és mai un problema de xarxa — és un problema local de la màquina, entre processos. La xarxa ni se n'ha assabentat.

I el seu cosí germà al client: si un client obre i tanca milers de connexions per segon cap a la mateixa destinació, pot arribar a esgotar els ports efímers (tots en TIME_WAIT). És estrany en una pime com Meridiano, però explica per què les aplicacions intensives reutilitzen connexions (connection pooling, HTTP keep-alive) en lloc d'obrir-ne una per petició.

TCP vs UDP com a decisió d'arquitectura

Triar transport no és una preferència: és una decisió de disseny que es pren responent dues preguntes — em puc permetre perdre dades? i em puc permetre esperar? Repassem els serveis reals de Meridiano:

Servei de Meridiano Transport Per què
Intranet HTTPS (GET /api/proyectos) TCP (443) Una resposta JSON amb un byte perdut és brossa: fiabilitat obligatòria
Fitxers compartits (SMB, servidor .10.10) TCP (445) Un document corrupte és inacceptable; a més són transferències llargues: la finestra de TCP les regula
Correu (SMTP/IMAP) TCP (587/993) Perdre un correu a mitges no és una opció
SSH al router i al servidor TCP (22) Cada tecla ha d'arribar, i en ordre
DNS (consultes de resolució) UDP (53) Pregunta i resposta caben en un datagrama; si es perd, el client torna a preguntar al cap d'uns 2 s. El handshake de TCP costaria més que la consulta sencera (TCP queda de reserva per a respostes grans)
DHCP (el DORA de 02-05) UDP (67/68) El client encara no té IP: impossible mantenir una connexió; són 4 datagrames de difusió
Videotrucades València–Bilbao UDP (RTP) Un fotograma que arriba tard ja no serveix: val més perdre'l que aturar el vídeo esperant una retransmissió. L'app ho corregeix a la seva manera (ajusta la qualitat)
Monitoratge del router (SNMP) UDP (161) Sondes petites i freqüents; una de perduda es repeteix al cicle següent

El patró que emergeix és fiable com una llei: dades que han d'arribar íntegres → TCP; temps real o intercanvis minúsculs on tornar-ho a provar surt més barat que garantir-ho → UDP. I una tercera via moderna que convé conèixer de nom: QUIC, el transport d'HTTP/3, que corre sobre UDP però reimplementa fiabilitat, ordre i xifratge a la mateixa aplicació — la prova que quan UDP "no dona garanties" en realitat està donant llibertat per construir les teves.

Errors Comuns i Consells

  • Creure que TCP lliura "missatges". Lliura un flux de bytes: dos send() poden arribar com un sol recv() o com diversos trossos. Les fronteres de missatge les defineix el protocol d'aplicació. Aquest malentès és la causa número u de bugs de xarxa en codi de principiants.
  • Espantar-se pels TIME_WAIT. Són el funcionament correcte del tancament TCP, no connexions penjades ni fuites. Preocupa't, en canvi, pels CLOSE-WAIT acumulats: aquests sí que indiquen que la teva aplicació no tanca sockets.
  • Buscar a la xarxa un "address already in use". És sempre un conflicte local entre processos per un port. ss -tlnp (o netstat -ano) identifica l'ocupant en segons.
  • Confondre el socket d'escolta amb les connexions. El LISTEN del port 443 és un; cada client acceptat té el seu propi socket ESTABLISHED. Tancar una connexió no afecta el LISTEN, i matar el LISTEN no talla les connexions ja establertes.
  • Triar UDP "perquè és més ràpid" sense pla B. UDP no és TCP-sense-llast: és un contracte diferent. Si tries UDP, la fiabilitat que necessitis la programes tu (o assumeixes les pèrdues, com fa el vídeo).
  • Consell: memoritza el trio ss -tln (què escolta?), ss -tn (què conversa?), ss -tlnp (quin és el procés?). Amb aquestes tres ordres es resol la majoria de dubtes de "està aixecat el servei?" abans de tocar res més.

Exercicis

  1. L'Ana executa al servidor de la intranet ss -tn i veu la línia SYN-SENT 0 1 192.168.10.10:41230 192.168.20.7:443 repetida durant 30 segons, fins que desapareix amb un error. (a) Qui està actuant com a client i qui com a servidor en aquest intent? (b) Què significa que l'estat es quedi clavat en SYN-SENT? (c) Dona dues hipòtesis compatibles, fent servir allò après a 03-05.

  2. Al pseudocodi del servidor de l'apartat 4, explica què passaria exactament (i què veuria la Marta des del seu navegador) si: (a) s'elimina la línia 3 (listen); (b) el programa es queda processant una petició lenta a la línia 6 i mentrestant arriben tres clients nous; (c) s'elimina la línia 8 (close) i el servidor atén centenars de peticions.

  3. Meridiano muntarà dos serveis nous: (a) un sistema de còpia de seguretat nocturna que puja els fitxers del servidor de València a un emmagatzematge a Bilbao a través de la VPN; (b) un panell a recepció que mostra la temperatura de la sala de servidors, actualitzada cada 2 segons des d'un sensor. Tria transport per a cadascun i justifica-ho amb les dues preguntes de l'apartat 8.

Solucions

  1. (a) El servidor de la intranet actua aquí com a client: el port d'origen és efímer (41230) i la destinació és el 443 del PC d'en Jon o d'algun equip de Bilbao (192.168.20.7) — probablement un chequeig o una integració. Els rols client/servidor són per connexió, no per màquina. (b) SYN-SENT clavat significa que es va enviar el SYN i no va arribar mai el SYN-ACK: el handshake no arrenca; el kernel reintenta el SYN diverses vegades i acaba rendint-se (aquest és l'error al cap d'uns 30 s). (c) Hipòtesis compatibles amb un timeout (03-05): la màquina .20.7 està apagada o inabastable (VPN caiguda? — comprovable amb ping), o un tallafoc descarta silenciosament els SYN cap a aquell port. Si el port simplement estigués tancat amb la màquina viva, hauria arribat un RST i l'error seria immediat ("connection refused"), no un timeout.

  2. (a) Sense listen, el socket no entra mai en estat LISTEN i el kernel respon als SYN entrants amb RST: la Marta veuria un "connexió rebutjada" immediat. (Segons el llenguatge, a més, l'accept de la línia 5 fallaria en executar-se.) (b) Res de greu a curt termini: el kernel completa els handshakes pel seu compte i encua les tres connexions ja establertes (dins del límit del backlog); els tres clients veuen la seva connexió oberta però sense resposta fins que el servidor acabi i torni a l'accept. Perceben lentitud, no rebuig — així es veu un servidor monofil saturat. Si la cua es desborda, els SYN següents sí que es descarten. (c) Cada connexió atesa queda oberta per sempre: els ESTABLISHED (i al client, esperes penjades) s'acumulen, i amb ells els descriptors de fitxer del procés, fins a esgotar el límit del SO i fer que accept comenci a fallar. És la fuita de sockets clàssica: es diagnostica veient centenars de connexions velles a ss -tn associades al procés.

  3. (a) Còpia de seguretat: TCP. Puc perdre dades? No — un backup corrupte és pitjor que no tenir backup. Puc esperar? Sí — és nocturn, la latència tant se val; i la finestra de TCP, a més, aprofitarà bé el túnel VPN regulant el ritme. (b) Panell de temperatura: UDP és raonable. Puc perdre dades? Sí — si es perd la lectura de les 10:00:02, la de les 10:00:04 la substitueix; la dada vella caduca sola. Puc esperar? No té sentit retransmetre una lectura obsoleta. Un datagrama petit cada 2 s sense handshake és l'ajust natural. (TCP tampoc no seria incorrecte aquí — amb tan poc trànsit gairebé no es notaria — però UDP expressa millor el disseny: l'última lectura guanya.)

Conclusió

La capa de transport és on la xarxa deixa de ser lampisteria i es converteix en interfície de programació: TCP i UDP són dos contractes de servei — flux fiable davant de datagrames sense promeses — i el socket és la porta per on qualsevol programa els fa servir, amb el ritual socket → bind → listen → accept al servidor i socket → connect al client, mentre el kernel fa la feina bruta de 02-04 per sota. Ja saps llegir l'estat d'aquesta maquinària (ss -tln per a allò que escolta, ss -tn per a allò que conversa), interpretar operativament LISTEN, ESTABLISHED i TIME_WAIT, desarmar un "address already in use" en dues ordres, i triar transport com el que és: una decisió d'arquitectura que es respon amb dues preguntes. Ens queda un últim esglaó a la pila: la capa d'aplicació, on viuen els protocols que la Marta i en Jon fan servir sense saber-ho — i on veurem, pas a pas, què passa des que el codi de la intranet demana GET /api/proyectos fins que aquells bytes entren al socket que acabes d'aprendre a obrir.

© Copyright 2026. Tots els drets reservats