Pokud ses někdy setkal s výrazem websocket a chtěl by ses dozvědět, co to vlastně je a jak se to používá v Python aplikaci, tak tento článek je právě pro tebe.
Standardně tvůj prohlížeč komunikuje na webu pomocí http protokolu. Klasický http protokol nabízí jednoduchou komunikaci. Pošle se request a jako odpověď dostanu response. Tento klasický komunikační způsob nebyl dostačující pro dnešní moderní aplikace. Byla potřeba pro komunikační kanál, který bude sloužit k obousměrné komunikaci.
HTTP by měl být víceméně bezstavový a klient a server mezi sebou komunikují jen když je třeba, jinak je spojení mezi nimi uzavřeno. Navíc, prohlížeč (klient) musí požádat server o komunikaci a server může na tuto žádost odpovědět. Ta žádost, to je ten http request. Jinak server neumí kontaktovat klienta jen tak sám od sebe.
U websocketů je tomu jinak. Jedná se o komunikační kanál, který se otevře jednou, na začátku a poté se používá ke komunikaci klienta a serveru v obou stranách. To znamená, že server může posílat data zároveň co klient posílá data na server. Toto se odborně jmenuje full-duplex.
Web socket má menší overheat přenosu dat, umí být real-time a hlavně, server může posílat data na klienta, aniž by si je klient musel explicitně vyžádat requestem. Toto je užitečné například u aplikací, které zobrazují real time data a server posílá tato data klientovi. Takže pokud nastane nějaká změna dat, server je prostě pošle na klienta.
Toto dříve nebylo možné provést pouze pomocí http protokolu.
Minimální pří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:
Nejlepší je vyzkoušet si tyto koncepty v praxi. Dnes budeme pracovat s Flaskem, knihovnou SocketIO a javascript knihovnami socket.io a jQuery. Budu předpokládat, že Flask aplikace alespoň trochu znáš.
Začneme tím, že si vytvoříme nové virtuální prostředí:
Nainstalujeme závislosti, které budeme potřebovat:
(venv)$ pip install flask, flask-socketio
V době psaní tohoto článku jsem používal verze Flask==1.0.2 a Flask-SocketIO=3.0.1. Když už máme připravené prostředí a nainstalované závislosti, uděláme nový soubor 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čátku máme importy jako pro každou jinou Flask aplikaci, avšak přibylo nám tam from flask_socketio import SocketIO. Tento naimportovaný modul je v podstatě totéž jako jiné Flask rozšíření .
Inicializaci websocketů ve Flask aplikací provedeme pomocí řádku socketio = SocketIO(app). Pomocí tohoto objektu socketio budeme přijímat a odesílat zprávy.
Minimální aplikace by měla mít alespoň jednu stránku. V našem případě to bude index.jinja. Toto je třeba, protože musíme poskytnout i klientskou část naší aplikace. Tam bude javascript knihovna socketio a nějaké další funkce.
Websockety umí přijímat a posílat zprávy. Provedeme zatím jen přijímání zpráv. Pomocí řádku socketio.on("event")definuji handler pro událost event. V tomto případě jednoduše vypíšu data na konzoli.
Posílání a přijímání dat na obou stranách (klient a server) probíhá jako event. Toto je důležitý fakt, protože architektura aplikace založené na eventech ( event driven architecture ) funguje trošku jinak než klasické volání funkce. Neříkám, abys měl z toho paniku teď, ale měj to na paměti.
Pokud znáš Flask aplikace, tak spuštění appky vypadá většinou takto
if __name__ == "__main__":
app.run("0.0.0.0", debug=True)
My ale musíme appku spustit jinak, jelikož používáme websockety. Spustíme ji pomocí objektu socketio, který jsme si vytvořili na začátku.
if __name__ == '__main__':
socketio.run(app, debug=True)
Nyní musíme ještě vytvořit 2 soubory. Snažíme se renderovat index.jinja a také musíme vytvořit hlavní javascript soubor, do kterého budeme psát klientskou část naší websocketové ukázky.
Vytvořím složku templates a do ní soubor index.jinja
Důležité jsou 3 importy v hlavičce html dokumentu. První importuje jQuery , druhý importuje knihovnu pro práci se sockety socketio a poslední import je pro náš main.js soubor, který musíme ještě vytvořit.
Jinak tento html dokument obsahuje pouze jeden formulář s jedním tlačítkem. To budeme používat k posílání zprávy přes websocket.
Vytvoříme složku static v ní js a v ní už konečně soubor main.js
Toto je hlavní logika klientské části. Z tadeto budeme přijímat a posílat zprávy přes websockety stejně jako na serverové části.
Pomocí řádku var socket = io.connect(url); se připojím na můj server. Následně pomocí jQuery upravím chování buttonu, aby při stisku poslal zprávu. K tomu slouží funkce socket.emit()
Okej, základ máme hotový a můžeme nyní zkoušet posílat zprávy. Aplikaci spustím pomocí pří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
Otevřu prohlížeč na http://localhost:5000 a zobrazí se mi jeden button. Když ho zmáčknu na konzole mi vyskočí:
test message
Pojďme tedy prozkoumat, jaké možnosti nám poskytuje tato knihovna socketio.
Přijímání zpráv
Jak jsem již zmiňoval, přijímání zpráv na obou stranách probíhá jako event. V Pythonu musíme pro takovýto event definovat handler. V javascriptu používáme tvz. callbacky. V principu jde o totéž, ale každý jazyk má své vlastní technické řešení a my si toho musíme být vědomi.
Každý event, který chci přijmout musím mít nějaké jméno. V příkladu jsme měli název event. Mohu ale použít cokoli
Namespace patří mezi další funkce, které mám knihovna SocketIO nabízí. Každý event si můžeme rozdělit podle namespace. To nám dává další možnosti organizace eventov.
Další vychytávka je to, že každý event, který pošleme, umí zavolat callback poté, co byl proveden. Například z javascriptu pošlu nějaká data na server a server mi ještě dodatečně potvrdí, že data byla zpracována. Aha takhle
Musím si otevřít v prohlížeči konzoli (já používám chrome) a když zmáčknu tlačítko, dostanu výpis na konzoli
Posílání zpráv
Zasílat eventy jsme již posílali, ale pouze z javascriptu. V Pythonu to vypadá velmi podobně. Používáme 2 funkce send a emit mezi nimiž je zásadní rozdíl.
Nejprve musíme importovat z knihovny flask-socketio
from flask_socketio import send
from flask_socketio import emit
Všimni si, že teď jsem použil handler, který zpracovává event s názvem message. Není to náhoda. Jde totiž o to, že funkce send posílá tvz. unnamed event . Tyto eventy se vždy posílají na handler, který zpracovává message.
Narozdíl od funkce send, funkce emit posílá již konkrétní event a musíš mu dát název. Zkusme tedy pozměnit náš příklad
Velmi užitečná funkce je broadcastování, což už z názvu vyplývá, že eventy se budou vysílat na všechny připojené klienty. Dejme tomu, že změníme funkci emit na broadcastování
Nyní, když si otevřeš 2 prohlížeče a v jednom zmáčkneš button, výsledek součtu se ukáže ve všech prohlížečích
note: callbacky se při broadcastování nebudou provádět
Závěr
Websockety mají mnoho využití. Tento článek byl jen úvod a přehled některých základních funkcí. V příštím blogu uděláme malou aplikaci postavenou na websocketech.
Máš nějaké dotazy k článku? Napiš ji do komentáře.
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).