Ak si sa niekedy stretol s výrazom websocket a chcel by si sa dozvedieť, čo to vlastne je a ako sa to používa v Python aplikácii, tak tento článok je práve pre teba.
Štandardne tvoj prehliadač komunikuje na webe pomocou http protokolu. Klasický http protokol ponúka jednoduchú komunikáciu. Pošle sa request a ako odpoveď dostanem response. Tento klasický komunikačný spôsob nebol dostatočný pre dnešné moderné aplikácie. Bola potreba pre komunikačný kanál, ktorý bude slúžiť na obojsmernú komunikáciu.
HTTP by mal byť viac-menej bezstavový a klient a server medzi sebou komunikujú iba keď treba, inak je spojenie medzi nimi uzavreté. Navyše, prehliadač (klient) musí požiadať server o komunikáciu a server môže na túto žiadosť odpovedať. Tá žiadosť, to je ten http request. Inak server nevie kontaktovať klienta len tak sám od seba.
Pri websocketoch je to inak. Ide o komunikačný kanál, ktorý sa otvorí raz, na začiatku a potom sa používa na komunikáciu klienta a servera v oboch stranách. To znamená, že server môže posielať dáta zároveň čo klient posiela dáta na server. Toto sa odborne volá full-duplex.
Web socket má menší overheat prenosu dát, vie byť real-time a hlavne, server môže posielať dáta na klienta bez toho, aby si ich klient musel explicitne vyžiadať requestom. Toto je užitočné napríklad pri aplikáciách, ktoré zobrazujú real time dáta a server posiela tieto dáta klientovi. Takže ak nastane nejaká zmena dát, server ich proste pošle na klienta.
Toto predtým nebolo možné spraviť iba pomocou http protokolu.
Minimálny príklad
Najlepšie je vyskúšať si tieto koncepty v praxi. Dnes budeme pracovať s Flaskom, knižnicou SocketIO a javascript knižnicami socket.io a jQuery. Budem predpokladať, že Flask aplikácie aspoň trochu poznáš.
Začneme tým, že si vytvoríme nové virtuálne prostredie:
Nainštalujeme závislosti, ktoré budeme potrebovať:
(venv)$ pip install flask, flask-socketio
V čase písania tohto článku som používal verzie Flask==1.0.2 a Flask-SocketIO=3.0.1.
Keď už máme pripravené prostredie a nainštalované závislosti, spravíme nový súbor server.py
from flask import Flask
from flask import render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config["SECRET_KEY"] = "secret"
socketio = SocketIO(app)
@app.route("/")
def index():
return render_template("index.jinja")
@socketio.on("event")
def handle_event(data):
print(data)
if __name__ == '__main__':
socketio.run(app, debug=True)
Na začiatku máme importy ako pre každú inú Flask aplikáciu, avšak pribudlo nám tam from flask_socketio import SocketIO. Tento naimportovaný modul je v podstate to isté ako iné Flask rozšírenia.
Inicializáciu websocketov vo Flask aplikácií spravíme pomocou riadku socketio = SocketIO(app). Pomocou tohto objektu socketio budeme príjmať a odosielať správy.
Minimálna aplikácia by mala mať aspoň jednu stránku. V našom prípade to bude index.jinja. Toto je treba pretože musíme poskytnúť aj klientskú časť našej aplikácie. Tam bude javascript knižnica socketio a nejaké ďalšie funkcie.
Websockety vedia prijímať a posielať správy. Spravíme zatiaľ len prijímanie správ. Pomocou riadku socketio.on("event")definujem handler pre udalosť event. V tomto prípade jednoducho vypíšem dáta na konzolu.
Posielanie a prijímanie dát na oboch stranách (klient a server) prebieha ako event. Toto je dôležitý fakt, pretože architektúra aplikácie založenej na eventoch (event driven architecture) funguje trošku inak ako klasické volanie funkcie. Nehovorím, aby si mal z toho paniku teraz, ale maj to na pamäti.
Ak poznáš Flask aplikácie, tak spustenie appky vyzerá zväčša takto
if __name__ == "__main__":
app.run("0.0.0.0", debug=True)
My ale musíme appku spustiť inak, pretože používame websockety. Spustíme ju pomocou objektu socketio, ktorý sme si vytvorili na začiatku.
if __name__ == '__main__':
socketio.run(app, debug=True)
Teraz musíme ešte vytvoriť 2 súbory. Snažíme sa renderovať index.jinja a taktiež musíme vytvoriť hlavný javascript súbor, do ktorého budeme písať klientskú časť našej websocketovej ukážky.
Vytvorím priečinok templates a do neho súbor index.jinja
Dôležité sú 3 importy v hlavičke html dokumentu. Prvý importuje jQuery, druhý importuje knižnicu na prácu so socketmi socketio a posledný import je pre náš main.js súbor, ktorý musíme ešte vytvoriť.
Inak, tento html dokument obsahuje len jeden formulár s jedným tlačítkom. To budeme používať na posielanie správy cez websocket.
Vytvoríme priečinok static v ňom js a v ňom už konečne súbor main.js
Toto je hlavná logika klientskej časti. Z tadeto budeme prijímať a posielať správy cez websockety tak isto ako na serverovej časti.
Pomocou riadku var socket = io.connect(url); sa pripojím na môj server. Následne pomocou jQuery upravím správanie buttonu, aby pri stlačení poslal správu. Na to slúži funkcia socket.emit()
Okej, základ máme hotový a môžeme teraz skúšať posielať správy. Aplikáciu spustím pomocou príkazu:
(venv)$ python server.py
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
* Serving Flask app "server" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
* Debugger is active!
* Debugger PIN: 478-618-530
Otvorím prehliadač na http://localhost:5000 a zobrazí sa mi jeden button. Keď ho stlačím na konzole mi vyskočí:
test message
Poďme teda preskúmať, aké možnosti nám poskytuje táto knižnica socketio.
Príjmanie správ
Ako som už spomínal, prijímanie správ na oboch stranách prebieha ako event. V Pythone musíme pre takýto event definovať handler. V javascripte používame tzv. callbacky. V princípe ide o to isté, ale každý jazyk má svoje vlastné technické riešenie a my si toho musíme byť vedomí.
Každý event, ktorý chcem prijať musím mať nejaké meno. V príklade sme mali názov event. Môžem ale použiť čokoľvek
Taktiež dáta sa automaticky menia na príslušný typ. Ak v javascripte pošlem string, tak string dostanem aj na serveri. Tak isto to platí pre iné dátové typy
Namespace patrí medzi ďalšie funkcie, ktoré mám knižnica SocketIO ponúka. Každý event si môžeme rozdeliť podľa namespaceov. To nám dáva ďalšie možnosti organizácie eventov.
Ďalšia vychytávka je to, že každý event, ktorý pošleme, vie zavolať callback potom, čo sa vykonal. Napríklad z javascriptu pošlem nejaké dáta na server a server mi ešte dodatočne potvrdí, že dáta boli spracované. Aha takto
Musím si otvoriť v prehliadači konzolu (ja používam chrome) a keď stlačím tlačítko, dostanem výpis na konzolu
Posielanie správ
Posielať eventy sme už posielali, ale iba z javascriptu. V Pythone to vyzerá veľmi podobne. Používame 2 funkcie send a emit medzi ktorými je zásadný rozdiel.
Najprv musíme importovať z knižnice flask-socketio
from flask_socketio import send
from flask_socketio import emit
Všimni si, že teraz som použil handler, ktorý spracováva event s názvom message. Nie je to náhoda. Ide totiž o to, že funkcia send posiela tzv. unnamed event. Tieto eventy sa vždy posielajú na handler, ktorý spracúva message.
Narozdiel od funkcie send, funkcia emit posiela už konkrétny event a musíš mu dať názov. Skúsme teda pozmeniť náš príklad
Veľmi užitočná funkcia je broadcastovanie, čo už z názvu vyplýva, že eventy sa budú vysielať na všetkých pripojených klientov. Dajme tomu, že zmeníme funciu emit na broadcastovanie
Teraz, keď si otvoríš 2 prehliadače a v jednom stlačíš button, výsledok súčtu sa ukáže vo všetkých prehliadačoch
note: callbacky sa pri broadcastovaní nebudú vykonávať
Záver
Websockety majú mnoho využití. Tento článok bol len úvod a prehľad niektorých základných funkcií. V budúcom blogu spravíme malú aplikáciu postavenú na websocketoch.
Máš nejaké otázky k článku? Napíš ju do komentára.
Ahoj, volám sa Miro a som Pythonista. Programovať som začal na strednej. Vtedy frčal ešte turbo pascal. Potom prišiel matfyz, kadejaké zveriny ako Haskell, no najviac sa mi zapáčil Python.
Od vtedy v Pythone robím všetko. Okrem vlastných vecí čo si programujem pre radosť, som pracoval v ESETe ako automatizér testovania. Samozrejme, všetko v Pythone. Potom som skočil do inej firmy, tam taktiež Python na automatické testovanie aj DevOps. Viacej krát som účinkoval ako speaker na PyCon.sk, kde som odovzdával svoje skúsenosti.
Medzi moje obľúbené oblasti teda parí DevOps, Automatizovanie testovania a web development (hlavne backend).