De bibliotheek websockets, zonder asyncio · eindproject
Eindproject: Project: de echoservice, opnieuw gebouwd
Je hebt een handler, een client en een manier om te lezen hoe een gesprek eindigde, elk apart geschreven en gecontroleerd. Wat je nog niet hebt, is een programma dat iemand kan draaien. In hoofdstuk 5 was dat programma loopback_demo.py met zijn echo_library.py, met nog drie
Wat je bouwt
echo_session(url, texts, *, connect=connect) is echo_once dat ook meldt hoe het gesprek eindigde. Maak verbinding met dezelfde drie grenzen en proxy=None. Zet binnen het with-blok alles in één try: stuur elke tekst en lees de echo ervan, zoals eerder; sluit daarna met connection.close(1000, "done") en roep nog één keer connection.recv(timeout=WAIT) aan. Zodra de sluithandshake voorbij is, werpt die recv ConnectionClosed op, en except ConnectionClosed as exc: geeft {"echoes": echoes, "close": describe_close(exc)} terug.
De try zetten doet ertoe voor het andere einde. De server die je schreef, sluit als eerste als hij MAX_MESSAGES bereikt, en een server kan verdwijnen. Hoe dan ook werpt de volgende recv of send ConnectionClosed op, en dezelfde except geeft de echo’s die wel aankwamen terug met de close die ze beëindigde: 1000 en message limit reached voor een server op zijn limiet, 1006 voor een die verdween.
parse_args(argv) leest dezelfde drie opties als het project van hoofdstuk 5, met dezelfde grenzen, en weigert alles daarbuiten met parser.error, dat het programma stopt met status 2:
--clients, eenintvan 1 tot en metMAX_CLIENTS(5), standaard 2;--messages, eenintvan 1 tot en metMAX_MESSAGES(10), standaard 3;--deadline, eenfloatboven 0 en hooguit 8 seconden, standaard 5.0.
main(argv=None, *, serve=serve, connect=connect, now=time.monotonic) zet het samen:
lees de opties met
parse_args(argv), en bereken wanneer de deadline valt:now()plus--deadline;bedien
echo_handlerop"127.0.0.1"0 in een with-statement, druk de poort af, en startserve_foreverop een thread, zoals inrun_echo;voor elke client, genummerd vanaf 1: als
now()de deadline heeft bereikt, druk dan af dat de client niet werd gestart en tel hem als mislukt; geef hem anders--messagesteksten naar keuze en draaiecho_sessionmet dews://127.0.0.1:PORT/echoenconnect=connect;als
echo_sessionOSErroropwerpt, druk dan af dat de client faalde en tel hem als mislukt, en ga daarna door naar de volgende client;ConnectionRefusedErrorenTimeoutErrorzijn allebei soortenOSError;druk anders één regel voor de client af, bijvoorbeeld hoeveel echo’s hij kreeg en de
, en tel hem alleen als geslaagd als hij elke echo kreeg en een close met 1000; join na het
with-blok de thread mettimeout=WAIT, en geef 0 terug als elke client slaagde, 1 als er een niet slaagde.
De formulering van je regels bepaal je zelf, en hoe je telt ook: een
Weer naden
serve= en connect= zijn connect= dat was in Networks with Python: zodat een aanroeper stand-ins kan meegeven. Submit geeft een serve mee waarvan de server noteert wat je ermee doet en niets bedient, en een connect die gescripte verbindingen uitdeelt: een die echoot, een waarvan de server na één echo sluit, een waarvan de server je close beantwoordt met 1001, een die een time-out krijgt, en een die geweigerd wordt. main moet zijn connect doorgeven aan echo_session; zonder connect=connect in die aanroep zou echo_session zijn standaardwaarde gebruiken, de echte connect van de bibliotheek.
now= is de geïnjecteerde klok uit Python in Practice. Submit geeft er een mee die voorbij de deadline springt zodra de eerste client verbinding heeft gemaakt, en verwacht dat er geen tweede client start.
Het afsluiten van de server heeft geen eigen code nodig. Omdat de server in een with-statement wordt gebruikt, roept het verlaten van het blok op elk pad shutdown() aan: na de laatste client, na een client die faalde, en zelfs als een
echo_session werpt TimeoutError op voor de tweede client. Wordt de server nog steeds afgesloten?
Run
Druk op Run. De laatste sectie van library_demo.py roept je main aan met --clients 2 --messages 2 en drukt af wat die teruggeeft. Een afgewerkte versie, gedraaid op de site, drukte dit af, waarbij PORT staat voor de poort die het systeem koos:
Lesson 6: main with two clients
listening on 127.0.0.1:PORT
client 1: 2 echoes, closed with 1000
client 2: 2 echoes, closed with 1000
0
Jouw regels luiden zoals je ze schreef; de 0 is de exitstatus die main teruggaf, en die moet 0 zijn. Standaardfout moet leeg zijn, zoals in les 4. Op je eigen computer, met de bibliotheek geïnstalleerd vanuit je requirements.txt, draait python echo_library.py --clients 3 hetzelfde programma vanuit een
Druk daarna op Submit. Voordat het de drie nieuwe functies controleert, controleert het de eerdere opnieuw: de handler, run_echo, echo_once en describe_close.
Als iets in main je verbaast, vraag het Monty:
Waarom geeft mijn main connect=connect mee aan echo_session als echo_session connect al als standaardwaarde heeft?
Nog steeds één tegelijk
Kijk wat main met de server van de bibliotheek doet: één client, dan de volgende. De server zou graag meerdere clients tegelijk bedienen, elk op een eigen handlerthread, maar dit programma vraagt hem dat nooit. Er is ook niets wat clients kunnen delen: elk praat alleen met zijn eigen echo. Hoofdstuk 7 verandert beide. Het bedient meerdere clients tegelijk en geeft ze iets gemeenschappelijks, het scorebord. Een scorebord dat netjes moet stoppen terwijl kijkers nog verbonden zijn, heeft meer nodig dan de manier waarop dit programma tussen clients stopt, bijvoorbeeld open verbindingen sluiten met 1001 als zijn deadline komt; het eindproject in hoofdstuk 10 regelt dat.
Opdracht
Schrijf drie echo_library.py, in deze volgorde. Submit noemt de eerste die nog NotImplementedError opwerpt.
echo_session(url, texts, *, connect=connect): maak verbinding zoalsecho_oncedoet, metopen_timeout=WAIT,close_timeout=WAITenproxy=None, in eenwith-statement. Stuur binnen ééntryelke tekst en lees de echo ervan metrecv(timeout=WAIT), en roep daarnaconnection.close(1000, "done")en nog één keerconnection.recv(timeout=WAIT)aan. Geef inexcept ConnectionClosed as exc:{"echoes": echoes, "close": describe_close(exc)}terug.parse_args(argv):--clients(int, 1 tot en metMAX_CLIENTS, standaard 2),--messages(int, 1 tot en metMAX_MESSAGES, standaard 3) en--deadline(float, boven 0 en hooguit 8, standaard 5.0), metparser.errorvoor eenbuiten de grenzen. main(argv=None, *, serve=serve, connect=connect, now=time.monotonic): lees de opties; stel de deadline in vanuitnow(); bedienecho_handlerop"127.0.0.1"0 in een with-statement en startserve_foreverop eenthreading.Thread; draai éénecho_sessionper client, na elkaar, met--messagesteksten enconnect=connect, en start geen client meer zodranow()de deadline heeft bereikt; tel een client dieOSErroropwerpt, minder echo’s krijgt dan hij stuurde, of een andere close dan 1000 krijgt, als mislukt; druk één regel per client af; join na het blok de thread mettimeout=WAIT; geef 0 terug als elke client slaagde en anders 1.
Druk op Run: de sectie van les 6 moet eindigen met 0, na één regel per client.
Druk daarna op Submit. De controle opent nooit een echo_handler, run_echo, echo_once en describe_close opnieuw, zoals les 2 tot en met 4 deden. Ze draait echo_session tegen stand-in-verbindingen: een echoserver (ze verwacht close(1000, "done") en {"echoes": [...], "close": {"code": 1000, "reason": "done"}}), een server die na één echo sluit, en een server die verdwijnt (1006), met dezelfde grenzen op connect en recv als in les 3. Ze draait parse_args op de standaardwaarden, de grootste toegestane waarden en waarden net voorbij elke grens. Ze draait main met een stand-in-serve, een stand-in-connect en een registrerende threading: serve(echo_handler, "127.0.0.1", 0), een thread op serve_forever gestart vóór de eerste client, één verbinding per client naar de poort die de server meldde, elk --messages teksten, het afsluiten na de clients en een join met een time-out na het afsluiten, en 0 voor twee goede clients. Ze verwacht 1, met de server toch afgesloten, voor een client met één echo van de twee, een close beantwoord met 1001, een geweigerde verbinding en een recv met een time-out; geen tweede client zodra haar klok voorbij de deadline gaat; en status 2 voor --clients 9. Je afgedrukte formulering wordt niet gecontroleerd.