Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fun with WebSocket and Flask

Fun with WebSocket and Flask

Presented at PyCon Ireland on October 22nd, 2017

WebSocket enables a web server to establish dedicated connections with clients, on which raw data can flow at any time and in any direction, free of the request/response cycle and the "chattiness" of the HTTP protocol. Paired with a high-level protocol such as Socket.IO, you get a powerful and easy to use framework for bi-directional, low latency communication, with clients available for Python, JavaScript, Java, iOS, Android, C++, .Net and more. Flask is a lightweight and very popular web framework for Python that makes writing web servers easy and fun, without sacrificing any power. What happens when you combine WebSocket, Socket.IO and Flask? In this talk I'm going to show you some of the cool applications that you can build when these technologies are put to work together, including streaming audio and video, real-time sensor data, automatic voting and more!

Miguel Grinberg

October 22, 2017
Tweet

More Decks by Miguel Grinberg

Other Decks in Programming

Transcript

  1. About Me • Software Dev @ Rackspace • Flask Mega-Tutorial

    • Flask Web Development book • APIs, Microservices, Socket.IO • Python, JavaScript, C++ • @miguelgrinberg • blog.miguelgrinberg.com • github.com/miguelgrinberg
  2. What is WebSocket? • Alternative to HTTP for client-server communication

    • Benefits over HTTP ◦ Dedicated connection ◦ Bi-directional: client and server can freely send a message to the other party ◦ Lightweight (works great on low powered devices such as the Raspberry Pi) • Supported in “most” desktop and mobile web browsers currently in use ◦ Internet Explorer, I’m looking at you!
  3. What is ? • Yet another client-server communication protocol for

    web applications • Built on top of WebSocket and HTTP • Benefits over plain WebSocket ◦ Downgrades to long-polling transparently if WebSocket is not available ◦ Socket.IO events are similar to function calls across client-server lines ◦ Reconnects automatically after an interruption ◦ The server can “broadcast” events to all clients or to groups of them • Official server implementation written in JavaScript • Official clients written in JavaScript, Swift, Java and C++ • Several community ports to other languages
  4. Socket.IO and Python • Python Socket.IO server: python-socketio • Adds

    Socket.IO to an existing web application ◦ Any WSGI compliant web server (Flask, Django, Bottle, Pyramid, etc.) ◦ Asyncio web servers based on aiohttp and sanic ◦ For Flask, flask-socketio provides improved integration • Python Socket.IO client: socketio-client
  5. WebSocket and Async • WebSocket servers maintain a long-term connection

    to each client • Async is the most practical way to support large number of active connections • WSGI apps must run on eventlet or gevent to get an async wrapper
  6. Let’s Learn Socket.IO By Example! • Source Repository: https://github.com/miguelgrinberg/socketio-examples •

    Five examples combined into a single application • All examples have a Python server and a JavaScript client • See README.md for setup instructions
  7. Chat Example var socketio = io.connect(location.origin); post_message.onchange = function(message) {

    socketio.emit('post-message', message); } socketio.on('message', function(message) { display_message(message); } socketio = SocketIO(app) @socketio.on('connect') def on_connect(): if not authenticate_user(): return False emit('message', user + 'has joined.', broadcast=True) @socketio.on('post-message') def on_post_message(message): emit('message', message, broadcast=True) @socketio.on('disconnect') def on_disconnect(): emit('message', user + 'has left.', broadcast=True) socketio.run(app) ← Client (JavaScript) Server (Python) →
  8. Audio Streaming Example function toggleRecording( e ) { if (!recording)

    { recording = true; socketio.emit('start-recording', {numChannels: 1, bps: 16, fps: 44100}); } else { recording = false; socketio.emit('end-recording'); } } socketio.on('add-wavefile', function(wavefile) { add_audio_to_page(wavefile); }); onaudioprocess = function(audioEvent) { if (recording) { socketio.emit('write-audio', audio_data); } } @socketio.on('start-recording') def start_recording(options): id = uuid.uuid4().hex session['wavename'] = id + '.wav' session['wavefile'] = wave.open(session['wavename']) configure_wavefile(session['wavefile'], options) @socketio.on('end-recording') def end_recording(): session['wavefile'].close() emit('add-wavefile', session['wavename'])) del session['wavefile'] del session['wavename'] @socketio.on('write-audio') def write_audio(data): session['wavefile'].writeframes(data) ← Client (JavaScript) Server (Python) →
  9. File Upload Example upload.onclick = function() { socketio.emit('start-transfer', filename, size,

    transfer_cb); } transfer_cb = function(filename) { if (!filename) abort_transfer(); for (var i = 0; i < num_chunks; i++) { chunk = read_next_chunk(); socketio.emit('write-chunk', filename, offset, chunk, chunk_cb); update_progress(offset + chunk.length); } } chunk_cb = function(ack) { if (!ack) abort_transfer(); } @socketio.on('start-transfer') def start_transfer(filename, size): _, ext = os.path.splitext(filename) if ext in ['.exe', '.bin', '.sh']: return False # reject the upload id = uuid.uuid4().hex create_file(id + ext, filename, size) return id + ext @socketio.on('write-chunk') def write_chunk(filename, offset, data): return write(filename, offset, data) ← Client (JavaScript) Server (Python) →
  10. Polls Example var socketio = io.connect(location.origin + '/polls'); function onButtonClick()

    { socketio.emit('vote', this.answer); } var socketio = io.connect(location.origin + '/polls-admin'); socketio.on('update-charts', function(results) { update_charts(results); }); @socketio.on('vote', namespace='/polls') def on_voter_vote(question, answer): add_or_update_vote(question, answer) @socketio.on('connect', namespace='/polls-admin') def on_admin_connect(): emit('update-charts', get_poll_results()) def update_task(): while True: emit('update-charts', get_poll_results(), broadcast=True, namespace='/polls-admin') socketio.sleep(5) socketio.start_background_task(update_task) ← Admin Client (JavaScript) ← Client (JavaScript) Server (Python) →
  11. @socketio.on('drop-pin', namespace='/where') def on_drop_pin(lat, lng): add_or_update_pin(lat, lng) emit('update-pin', lat, lng,

    broadcast=True, namespace='/where-admin') @socketio.on('connect', namespace='/polls-admin') def on_admin_connect(): emit('update-pins', get_all_pins()) Maps Example var socketio = io.connect(location.origin + '/polls'); function onMapClick() { socketio.emit('drop-pin', lat, lng); } var socketio = io.connect(location.origin + '/polls-admin'); socketio.on('update-pin', function(lat, lng) { update_pin_on_map(lat, lng); }); socketio.on('update-pins', function(pins) { for (var i = 0; i < pins.length; i++) update_pin_on_map(pins[i].lat, pins[i].lng); }); ← Admin Client (JavaScript) ← Client (JavaScript) Server (Python) →
  12. Your Turn To Have Fun! • I hope this talk

    motivates you to play with Socket.IO! • Full documentation and several examples available in the GitHub Repos ◦ miguelgrinberg/python-socketio ◦ miguelgrinberg/flask-socketio ◦ miguelgrinberg/socketio-examples • I answer Python Socket.IO questions on Stack Overflow, Twitter, etc. • I gladly (desperately?) accept contributions! ◦ New features ◦ Wrappers for more web frameworks ◦ Cool examples or tutorials ◦ Documentation fixes and improvements