0%

De bibliotheek websockets, zonder asyncio · oefening

Verbinding maken als client

Je bibliotheekserver heeft tot nu toe alleen met de zelfgebouwde client uit hoofdstuk 5 gepraat. Deze les schrijft de andere kant met de bibliotheek, echo_once(url, texts, *, connect=connect), en richt die ene client daarna op beide servers: die van jou uit les 2 en de server uit hoofdstuk 5.

Een URL in plaats van een adres

De client van de bibliotheek neemt een aan, geen host en . Lees ws://127.0.0.1:50123/echo zoals with Python je leerde een URL uit elkaar te halen. Het schema ws betekent over gewone ; wss zou toevoegen, dat deze cursus niet gebruikt. 127.0.0.1 en 50123 zijn de host en de poort waarmee verbinding wordt gemaakt, en /echo is het pad.

Uit die delen bouwt de bibliotheek het upgradeverzoek dat je in hoofdstuk 2 met de hand bouwde met build_upgrade_request: een GET-verzoekregel met het pad als doel, een Host-header, een nieuwe Sec-WebSocket-Key en Sec-WebSocket-Version: 13. Als de 101 aankomt, controleert ze de van Sec-WebSocket-Accept tegen haar sleutel, het werk dat je check_accept_response deed.

Welke verzoekregel stuurt de bibliotheek voor ws://127.0.0.1:50123/echo?

Aan elke kant begrensd

connect, uit websockets.sync.client, opent de TCP-verbinding, voert de uit, en geeft een verbinding terug met dezelfde send, recv en close die je handler gebruikte. Een client wacht op drie plekken, en deze cursus begrenst elk ervan met WAIT, 2 seconden:

  • open_timeout=WAIT begrenst de handshake. De eigen standaardwaarde van de bibliotheek is 10 seconden.

  • recv(timeout=WAIT) begrenst elke echo, zoals in je handler.

  • close_timeout=WAIT begrenst de sluithandshake. De standaardwaarde daarvan is ook 10 seconden.

Een grens maakt van een wachttijd een . Als een server de TCP-verbinding accepteert maar de upgrade nooit beantwoordt, werpt connect TimeoutError op met de melding timed out while waiting for handshake response zodra open_timeout verstreken is. In een lokale test met open_timeout=0.5 tegen een listener die nooit antwoordde, duurde dat ongeveer een halve seconde.

Op elke connect komt nog één : proxy=None. Standaard leest de bibliotheek proxy-instellingen uit de omgeving, zoals de HTTP_PROXY, en volgt die zelfs voor 127.0.0.1. In een lokale test met HTTP_PROXY ingesteld op een poort waar niets luisterde, ging een verbinding die voor een -server bedoeld was in plaats daarvan naar dat proxyadres en faalde meteen; met proxy=None bereikte dezelfde verbinding de server. Of een machine zo’n variabele instelt, is niet iets waar een loopback-programma van mag afhangen, dus elke connect in deze cursus geeft proxy=None mee.

Gebruik connect(...) altijd in een with-statement. De versie die je in les 1 vastpinde, geeft een DeprecationWarning als connect op een andere manier wordt aangeroepen.

Waarom geeft elke connect in deze cursus proxy=None mee?

Het blok verlaten

Het gesprek van een client staat binnen het with-blok:

with connect(url, open_timeout=WAIT, close_timeout=WAIT, proxy=None) as connection:
    connection.send("hello")

Als het blok eindigt, op welk pad ook, stuurt de client een close- met 1000 en een lege reden, en wacht hij daarna, hooguit close_timeout, op de beantwoordende close van de server en op het sluiten van de TCP-verbinding door de server. Dat is de sluithandshake die je in hoofdstuk 5 schreef met close_frame en wait_for_close, nu voor je uitgevoerd.

Wat gebeurt er als het blok with connect(...) eindigt?

Je opdracht

Schrijf echo_once(url, texts, *, connect=connect) in echo_library.py. Open de verbinding met connect(url, open_timeout=WAIT, close_timeout=WAIT, proxy=None) in een with-statement. Stuur voor elke tekst die tekst en lees de echo ervan met connection.recv(timeout=WAIT), en voeg de echo toe aan een . Geef de lijst na het blok terug, zodat de verbinding dan gesloten is. Eerst elke tekst sturen en daarna de echo’s lezen, geeft hier dezelfde lijst; één keer sturen, dan één keer lezen, houdt elke echo naast de tekst die hij beantwoordt. connect=connect is dezelfde soort naad als serve=serve: Submit geeft een stand-in mee.

Voordat je op Run drukt

Run drukt deze keer twee van de blokken uit les 2 af, elk beginnend met connection handler failed, één voor elk gesprek dat je handler uit les 2 bedient. Het eerste komt uit de sectie van les 2, zoals eerder. Het tweede komt uit de eerste sectie van les 3: je client verlaat zijn with-blok terwijl je handler in recv wacht, dus de handler loopt weer tegen een close aan, deze keer zonder reden. Dat blok eindigt met:

websockets.exceptions.ConnectionClosedOK: received 1000 (OK); then sent 1000 (OK)

Beide zijn te verwachten, Submit accepteert ze nog steeds, en les 4 haalt ze weg.

Druk op Run. Na de sectie van les 2 drukte een afgewerkte versie, gedraaid op de site, af:

Lesson 3: your library client, your library server
['hello', 'café ✓', 'third message']
Lesson 3: your library client, the Chapter 5 server
{'client': ['hello', 'café ✓', 'third message'], 'Chapter 5 server': [{'messages': 3, 'code': 1000}]}

De eerste sectie is de bibliotheek aan beide kanten. De tweede is opnieuw de interopcontrole van les 1, deze keer met je eigen client: de server uit hoofdstuk 5 meldt drie berichten en een close met 1000. Druk daarna op Submit.

Beide gesprekken eindigen met een aan de kant van de server, ook al ging er niets mis. De volgende les leest hoe een verbinding eindigde, en laat de handler stil eindigen.

Opdracht

Schrijf echo_once(url, texts, *, connect=connect) in echo_library.py:

  1. open de verbinding met connect(url, open_timeout=WAIT, close_timeout=WAIT, proxy=None) in een with-statement;

  2. stuur elke tekst met connection.send en lees de echo ervan met connection.recv(timeout=WAIT), en voeg de echo toe aan een ;

  3. geef de lijst terug.

Druk op Run: de twee secties van les 3 moeten ['hello', 'café ✓', 'third message'] afdrukken en dezelfde echo’s met het verslag van de server uit hoofdstuk 5, [{'messages': 3, 'code': 1000}]. Er verschijnen twee blokken connection handler failed op standaardfout, zoals de les beschrijft.

Druk daarna op Submit. De controle opent nooit een . Ze geeft echo_once een stand-in-connect en noteert hoe die wordt aangeroepen: de url ongewijzigd doorgegeven, open_timeout en close_timeout elk ingesteld en hooguit 2 seconden, en proxy=None. De stand-in-verbindingen beantwoorden teksten die je nog niet hebt gezien, en een ervan beantwoordt elke tekst in hoofdletters, dus de lijst die je teruggeeft, moet bevatten wat recv teruggaf, op volgorde. Elke recv heeft een time-out van hooguit 2 seconden nodig, één recv per tekst, en de verbinding moet in een with-statement worden gebruikt. Alle teksten sturen voordat je alle echo’s leest, is prima.