9.10. TCP – un flux fiabil de octeți

TCP, Transmission Control Protocol, este celălalt serviciu de la nivelul transport, construit deasupra IP. Dacă UDP poate fi descris cel mai bine prin „trimite un pachet și speră”, TCP înseamnă „deschide o conexiune între două capete și tratează-o ca pe o conductă bidirecțională de octeți pe care cealaltă parte îi primește cu siguranță, în ordine și exact o singură dată”. Cea mai mare parte a traficului din internet îl folosește, la fel ca și cea mai mare parte a activității de rețea a camerei.

9.10.1. Ce adaugă TCP la IP

TCP face mult mai mult decât UDP. El adaugă:

  • O conexiune. Înainte ca vreun fel de date să circule, cele două capete schimbă o scurtă strângere de mână pentru a conveni că discută. Conexiunea are o stare – „deschisă”, „semi-închisă”, „închisă” – pe care ambele părți o urmăresc.

  • Livrare fiabilă. Fiecare octet introdus de expeditor este confirmat de receptor. Tot ce nu este confirmat într-un interval de timeout este trimis din nou. Aplicația nu vede niciodată un octet pierdut; vede doar o întârziere, în timp ce protocolul retrimite.

  • Livrare în ordine. Octeții sosesc în aceeași ordine în care au fost trimiși. Chiar dacă pachetele ajung la receptor în dezordine, TCP le reordonează înainte ca aplicația să citească.

  • Controlul fluxului. Dacă receptorul este lent, expeditorul este înștiințat să încetinească; conexiunea se adaptează la ritmul capătului mai slab.

  • Controlul congestiei. Dacă rețeaua intermediară începe să piardă pachete, expeditorul se retrage până când lucrurile revin la normal. Astfel, o singură conexiune nu poate să blocheze o legătură saturată.

Toate acestea sunt automate. API-ul Python folosit de aplicație se rezumă la send(bytes) și recv(n); TCP se ocupă de tot restul, dedesubt.

9.10.2. Strângerea de mână

O conexiune TCP se deschide printr-un schimb în trei pași, înainte de a permite trecerea oricăror date:

O diagramă cu două coloane etichetate "client" și "server". O săgeată de la client la server etichetată "SYN", apoi o săgeată de la server la client etichetată "SYN-ACK", apoi o săgeată de la client la server etichetată "ACK". Sub acestea, o săgeată groasă etichetată "date care circulă în ambele sensuri".

Strângerea de mână în trei pași. Odată ce ambele părți au confirmat, conexiunea este deschisă și datele pot circula.

Clientul trimite un pachet SYN (synchronise) prin care cere deschiderea. Serverul răspunde cu SYN-ACK (synchronise + acknowledge), acceptând. Clientul trimite un ACK final pentru confirmare. După acest dus-întors, ambele părți convin că conexiunea este deschisă și și-au sincronizat contoarele pentru a urmări care octeți au fost trimiși și recepționați.

Strângerea de mână costă un timp de un dus-întors (round-trip-time) de latență înainte ca primul octet util să treacă. Pentru rețelele locale, acesta este de o milisecundă; pentru conexiunile intercontinentale, este de aproximativ 100 ms. Acesta este costul principal al TCP și motivul pentru care mesajele scurte, de unică folosință, sunt uneori mai bine servite de UDP.

9.10.2.1. Strângerea de mână de închidere

Conexiunile TCP se închid, de asemenea, printr-un schimb (un FIN din fiecare parte). Fiecare capăt își poate închide independent jumătatea sa de conexiune; o stare semi-închisă, în care o parte a terminat de trimis, dar cealaltă încă discută, este legitimă, deși neobișnuită. În mod normal, aplicația apelează pur și simplu close() și lasă protocolul să gestioneze secvența de închidere.

9.10.3. Ce nu garantează TCP

Câteva lucruri pe care TCP este uneori presupus a le face, dar nu le face:

  • Limitele mesajelor. Aplicația trimite un flux de octeți, nu un flux de mesaje. Două apeluri send(b"hello") ar putea sosi ca un singur recv() de b"hellohello", sau ca două apeluri recv()de dimensiuni variate. Dacă aplicația dorește încadrarea mesajelor, trebuie să adauge ea însăși această încadrare (un caracter newline, un prefix de lungime, orice). Trimiterea de documente JSON prin TCP, de exemplu, necesită ca fiecare document să fie separat de un newline sau de un alt marcaj.

  • Criptarea. TCP transportă octeții pe care i-a primit de la aplicație în clar, pe tot parcursul. Dacă este nevoie ca acel conținut să fie confidențial, aplicația trebuie să învelească conexiunea în TLS (vezi Socluri criptate și TLS).

  • Autentificarea. TCP se asigură că octeții au sosit intacți. Nu spune nimic despre cine i-a trimis. Autentificarea este, de asemenea, o preocupare de nivel superior.

  • Activitatea pe o conexiune inactivă. Dacă niciuna dintre părți nu trimite date pentru un timp îndelungat, conexiunea este tehnic încă deschisă, dar nu poate detecta dacă celălalt capăt a căzut sau a dispărut. Sondele keepalive pot fi activate pe socket pentru a rezolva acest lucru atunci când contează.

9.10.4. Când să folosești TCP

TCP este răspunsul corect pentru aproape orice conversație care se încadrează în tiparul „clientul deschide o conexiune către un server, schimbă niște octeți, iar conexiunea se închide când s-a terminat”. Cererile HTTP și HTTPS, sesiunile SSH, interogările de baze de date, transferurile de fișiere, încărcările de imagini – toate prin TCP.

Apelează la UDP doar atunci când conversația nu se încadrează în acel tipar: mesaje independente, autonome, în care pierderea este acceptabilă, trafic multicast, sincronizarea timpului, căutările de nume sau cazurile extrem de sensibile la latență, în care costul strângerii de mână este prohibitiv.

Cu porturi, UDP și TCP toate puse pe masă, povestea nivelului transport este completă. API-ul Python care le expune pe ambele se află pe pagina următoare: Obiecte socket.