9.10. TCP – een betrouwbare stroom van bytes¶
TCP, het Transmission Control Protocol, is de andere transportlaagdienst bovenop IP. Waar UDP het best te omschrijven is als “verstuur een pakket en hoop maar”, is TCP “open een verbinding tussen twee eindpunten en behandel die als een tweerichtingsbuis van bytes die de andere kant met zekerheid ontvangt, in volgorde en precies eenmaal”. Het grootste deel van het internetverkeer gebruikt het, en het meeste van wat de camera op het netwerk doet, gebruikt het ook.
9.10.1. Wat TCP toevoegt aan IP¶
TCP doet veel meer dan UDP. Het voegt het volgende toe:
Een verbinding. Voordat er gegevens stromen, wisselen de twee eindpunten een korte handshake uit om af te spreken dat ze met elkaar praten. De verbinding heeft een toestand – “open”, “half gesloten”, “gesloten” – die beide kanten bijhouden.
Betrouwbare aflevering. Elke byte die de verzender erin stopt, wordt door de ontvanger bevestigd. Alles wat niet binnen een time-out wordt bevestigd, wordt opnieuw verstuurd. De applicatie ziet nooit een verloren byte; ze ziet een vertraging terwijl het protocol opnieuw verstuurt.
Aflevering in volgorde. Bytes komen aan in dezelfde volgorde waarin ze verstuurd zijn. Zelfs als pakketten in een verkeerde volgorde bij de ontvanger aankomen, zet TCP ze weer op volgorde voordat de applicatie ze leest.
Flow control. Als de ontvanger traag is, krijgt de verzender te horen dat hij moet vertragen; de verbinding past zich aan het tempo van de zwakkere kant aan.
Congestiebeheer. Als het netwerk er tussenin pakketten begint te laten vallen, trekt de verzender zich terug totdat alles zich herstelt. Dit voorkomt dat een enkele verbinding een verzadigde verbinding plat legt.
Dit alles gebeurt automatisch. De Python-API die de applicatie gebruikt is simpelweg send(bytes) en recv(n); TCP regelt al het andere eronder.
9.10.2. De handshake¶
Een TCP-verbinding wordt geopend met een uitwisseling in drie stappen voordat er gegevens doorheen mogen:
De handshake in drie stappen. Zodra beide kanten hebben bevestigd, is de verbinding open en kunnen er gegevens stromen.¶
De client stuurt een SYN-pakket (synchroniseren) met het verzoek om te openen. De server antwoordt met SYN-ACK (synchroniseren + bevestigen) en accepteert. De client stuurt een laatste ACK ter bevestiging. Na deze heen-en-terugreis zijn beide kanten het erover eens dat de verbinding open is en hebben ze hun tellers gesynchroniseerd voor het bijhouden van welke bytes verstuurd en ontvangen zijn.
De handshake kost één round-trip-time aan latentie voordat de eerste nuttige byte er doorheen komt. Voor lokale netwerken is dat een milliseconde; voor verbindingen over een continent is dat ongeveer 100 ms. Dit is de belangrijkste kostenpost van TCP en de reden waarom korte, eenmalige berichten soms beter af zijn met UDP.
9.10.2.1. De afsluitende handshake¶
TCP-verbindingen worden ook met een uitwisseling gesloten (een FIN van elke kant). Elk uiteinde kan zijn helft van de verbinding onafhankelijk sluiten; een half gesloten toestand waarin één kant klaar is met verzenden maar de andere nog praat, is toegestaan, hoewel ongebruikelijk. De applicatie roept normaal gesproken gewoon close() aan en laat het protocol de afsluitprocedure afhandelen.
9.10.3. Wat TCP niet garandeert¶
Een paar dingen waarvan soms wordt aangenomen dat TCP ze doet, maar wat het niet doet:
Berichtgrenzen. De applicatie stuurt een stroom van bytes, geen stroom van berichten. Twee
send(b"hello")-aanroepen kunnen aankomen als éénrecv()vanb"hellohello", of als tweerecv()s van verschillende groottes. Als de applicatie berichtindeling wil, moet ze die indeling zelf toevoegen (een nieuwe regel, een lengteprefix, wat dan ook). Het versturen van JSON-documenten over TCP vereist bijvoorbeeld dat elk document gescheiden wordt door een nieuwe regel of een andere markering.Versleuteling. TCP draagt de bytes die de applicatie eraan gaf, onversleuteld, helemaal door. Als de inhoud vertrouwelijk moet zijn, moet de applicatie de verbinding in TLS verpakken (zie Versleutelde sockets en TLS).
Authenticatie. TCP zorgt ervoor dat de bytes ongeschonden aankomen. Het zegt niets over wie ze verstuurd heeft. Authenticatie is ook een zaak van een hogere laag.
Levendigheid op een stille verbinding. Als geen van beide kanten lange tijd gegevens stuurt, is de verbinding technisch gezien nog steeds open, maar kan niet worden gedetecteerd dat het andere uiteinde is gecrasht of verdwenen. Keepalive-sondes kunnen op de socket worden ingeschakeld om dit op te lossen wanneer het ertoe doet.
9.10.4. Wanneer TCP gebruiken¶
TCP is het juiste antwoord voor vrijwel elke conversatie die past in de vorm “client opent een verbinding met een server, ze wisselen wat bytes uit, de verbinding wordt gesloten als het klaar is”. HTTP- en HTTPS-verzoeken, SSH-sessies, databasequery’s, bestandsoverdrachten, afbeeldingsuploads – allemaal via TCP.
Grijp alleen naar UDP wanneer de conversatie niet in die vorm past: onafhankelijke, op zichzelf staande berichten waarbij verlies acceptabel is, multicastverkeer, tijdsynchronisatie, naamopzoekingen, of extreem latentiegevoelige gevallen waarin de handshakekosten te hoog zijn.
Met poorten, UDP en TCP allemaal op tafel is het verhaal van de transportlaag af. De Python-API die beide blootlegt, staat op de volgende pagina: Socketobjecten.