Getting Started¶
Initialization¶
The following code example shows how to add Flask-SocketIO to a Flaskapplication:
fromflaskimportFlask,render_templatefromflask_socketioimportSocketIOapp=Flask(__name__)app.config['SECRET_KEY']='secret!'socketio=SocketIO(app)if__name__=='__main__':socketio.run(app)
Theinit_app()
style of initialization is also supported:
fromflaskimportFlask,render_templatefromflask_socketioimportSocketIOsocketio=SocketIO()defcreate_app():app=Flask(__name__)app.config['SECRET_KEY']='secret!'socketio.init_app(app)returnappif__name__=='__main__':app=create_app()socketio.run(app)
To start the web server simply execute your script. Note the way the web serveris started. Thesocketio.run()
function encapsulates the start up of theweb server and replaces theapp.run()
standard Flask development serverstart up. When the application is in debug mode the Werkzeug development serveris still used and configured properly insidesocketio.run()
. In productionmode the eventlet web server is used if available, else the gevent web serveris used. If eventlet and gevent are not installed, the Werkzeug development webserver is used.
Theflaskrun
command introduced in Flask 0.11 can be used to start aFlask-SocketIO development server based on Werkzeug, but this method of startingthe Flask-SocketIO server is not recommended due to lack of WebSocket support.Previous versions of this package included a customized version of theflaskrun
command that allowed the use of WebSocket on eventlet and geventproduction servers, but this functionality has been discontinued in favor of thesocketio.run(app)
startup method shown above which is more robust.
The application must serve a page to the client that loads the Socket.IOlibrary and establishes a connection:
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA=="crossorigin="anonymous"></script><scripttype="text/javascript"charset="utf-8">varsocket=io();socket.on('connect',function(){socket.emit('my event',{data:'I\'m connected!'});});</script>
Receiving Messages¶
When using SocketIO, messages are received by both parties as events. On theclient side Javascript callbacks are used. With Flask-SocketIO the serverneeds to register handlers for these events, similarly to how routes arehandled by view functions.
The following example creates a server-side event handler for an unnamedevent:
@socketio.on('message')defhandle_message(data):print('received message: '+data)
The above example uses string messages. Another type of unnamed events useJSON data:
@socketio.on('json')defhandle_json(json):print('received json: '+str(json))
The most flexible type of event uses custom event names. The message data forthese events can be string, bytes, int, or JSON:
@socketio.on('my event')defhandle_my_custom_event(json):print('received json: '+str(json))
Custom named events can also support multiple arguments:
@socketio.on('my_event')defhandle_my_custom_event(arg1,arg2,arg3):print('received args: '+arg1+arg2+arg3)
When the name of the event is a valid Python identifier that does not collidewith other defined symbols, the@socketio.event
decorator provides a morecompact syntax that takes the event name from the decorated function:
@socketio.eventdefmy_custom_event(arg1,arg2,arg3):print('received args: '+arg1+arg2+arg3)
Named events are the most flexible, as they eliminate the need to includeadditional metadata to describe the message type. The namesmessage
,json
,connect
anddisconnect
are reserved and cannot be used fornamed events.
Flask-SocketIO also supports SocketIO namespaces, which allow the client tomultiplex several independent connections on the same physical socket:
@socketio.on('my event',namespace='/test')defhandle_my_custom_namespace_event(json):print('received json: '+str(json))
When a namespace is not specified a default global namespace with the name'/'
is used.
For cases when a decorator syntax isn’t convenient, theon_event
methodcan be used:
defmy_function_handler(data):passsocketio.on_event('my event',my_function_handler,namespace='/test')
Clients may request an acknowledgement callback that confirms receipt of amessage they sent. Any values returned from the handler function will bepassed to the client as arguments in the callback function:
@socketio.on('my event')defhandle_my_custom_event(json):print('received json: '+str(json))return'one',2
In the above example, the client callback function will be invoked withtwo arguments,'one'
and2
. If a handler function does not return anyvalues, the client callback function will be invoked without arguments.
Sending Messages¶
SocketIO event handlers defined as shown in the previous section can sendreply messages to the connected client using thesend()
andemit()
functions.
The following examples bounce received events back to the client that sentthem:
fromflask_socketioimportsend,emit@socketio.on('message')defhandle_message(message):send(message)@socketio.on('json')defhandle_json(json):send(json,json=True)@socketio.on('my event')defhandle_my_custom_event(json):emit('my response',json)
Note howsend()
andemit()
are used for unnamed and named eventsrespectively.
When working with namespaces,send()
andemit()
use the namespace ofthe incoming message by default. A different namespace can be specified withthe optionalnamespace
argument:
@socketio.on('message')defhandle_message(message):send(message,namespace='/chat')@socketio.on('my event')defhandle_my_custom_event(json):emit('my response',json,namespace='/chat')
To send an event with multiple arguments, send a tuple:
@socketio.on('my event')defhandle_my_custom_event(json):emit('my response',('foo','bar',json),namespace='/chat')
SocketIO supports acknowledgment callbacks that confirm that a message wasreceived by the client:
defack():print('message was received!')@socketio.on('my event')defhandle_my_custom_event(json):emit('my response',json,callback=ack)
When using callbacks, the Javascript client receives a callback function toinvoke upon receipt of the message. After the client application invokes thecallback function the server invokes the corresponding server-side callback.If the client-side callback is invoked with arguments, these are provided asarguments to the server-side callback as well.
Broadcasting¶
Another very useful feature of SocketIO is the broadcasting of messages.Flask-SocketIO supports this feature with thebroadcast=True
optionalargument tosend()
andemit()
:
@socketio.on('my event')defhandle_my_custom_event(data):emit('my response',data,broadcast=True)
When a message is sent with the broadcast option enabled, all clientsconnected to the namespace receive it, including the sender. When namespacesare not used, the clients connected to the global namespace receive themessage. Note that callbacks are not invoked for broadcast messages.
In all the examples shown until this point the server responds to an eventsent by the client. But for some applications, the server needs to be theoriginator of a message. This can be useful to send notifications to clientsof events that originated in the server, for example in a background thread.Thesocketio.send()
andsocketio.emit()
methods can be used tobroadcast to all connected clients:
defsome_function():socketio.emit('some event',{'data':42})
Note thatsocketio.send()
andsocketio.emit()
are not the samefunctions as the context-awaresend()
andemit()
. Also note that in theabove usage there is no client context, sobroadcast=True
is assumed anddoes not need to be specified.
Rooms¶
For many applications it is necessary to group users into subsets that can beaddressed together. The best example is a chat application with multiple rooms,where users receive messages from the room or rooms they are in, but not fromother rooms where other users are. Flask-SocketIO supports this concept ofrooms through thejoin_room()
andleave_room()
functions:
fromflask_socketioimportjoin_room,leave_room@socketio.on('join')defon_join(data):username=data['username']room=data['room']join_room(room)send(username+' has entered the room.',to=room)@socketio.on('leave')defon_leave(data):username=data['username']room=data['room']leave_room(room)send(username+' has left the room.',to=room)
Thesend()
andemit()
functions accept an optionalto
argumentthat cause the message to be sent to all the clients that are in the givenroom.
All clients are assigned a room when they connect, named with the session IDof the connection, which can be obtained fromrequest.sid
. A given clientcan join any rooms, which can be given any names. When a client disconnects itis removed from all the rooms it was in. The context-freesocketio.send()
andsocketio.emit()
functions also accept ato
argument to broadcastto all clients in a room.
Since all clients are assigned a personal room, to address a message to asingle client, the session ID of the client can be used as theto
argument.
Connection Events¶
Flask-SocketIO also dispatches connection and disconnection events. Thefollowing example shows how to register handlers for them:
@socketio.on('connect')deftest_connect(auth):emit('my response',{'data':'Connected'})@socketio.on('disconnect')deftest_disconnect(reason):print('Client disconnected, reason:',reason)
Theauth
argument in the connection handler is optional. The client canuse it to pass authentication data such as tokens in dictionary format. If theclient does not provide authentication details, then this argument is set toNone
. If the server defines a connection event handler without thisargument, then any authentication data passed by the client is discarded.
The connection event handler can returnFalse
to reject the connection, orit can also raiseConnectionRefusedError. This is so that the client can beauthenticated at this point. When using the exception, any arguments passed tothe exception are returned to the client in the error packet. Examples:
fromflask_socketioimportConnectionRefusedError@socketio.on('connect')defconnect():ifnotself.authenticate(request.args):raiseConnectionRefusedError('unauthorized!')
The disconnection event handler receives areason
argument that indicatesthe cause of the disconnection. Theflask_socketio.SocketIO.reason
member includes constants for all the possible reasons.
Note that connection and disconnection events are sent individually on eachnamespace used.
Class-Based Namespaces¶
As an alternative to the decorator-based event handlers described above, theevent handlers that belong to a namespace can be created as methods of aclass. Theflask_socketio.Namespace
is provided as a base class tocreate class-based namespaces:
fromflask_socketioimportNamespace,emitclassMyCustomNamespace(Namespace):defon_connect(self):passdefon_disconnect(self,reason):passdefon_my_event(self,data):emit('my_response',data)socketio.on_namespace(MyCustomNamespace('/test'))
When class-based namespaces are used, any events received by the server aredispatched to a method named as the event name with theon_
prefix. Forexample, eventmy_event
will be handled by a method namedon_my_event
.If an event is received for which there is no corresponding method defined inthe namespace class, then the event is ignored. All event names used inclass-based namespaces must use characters that are legal in method names.
As a convenience to methods defined in a class-based namespace, the namespaceinstance includes versions of several of the methods in theflask_socketio.SocketIO
class that default to the proper namespacewhen thenamespace
argument is not given.
If an event has a handler in a class-based namespace, and also adecorator-based function handler, only the decorated function handler isinvoked.
Error Handling¶
Flask-SocketIO can also deal with exceptions:
@socketio.on_error()# Handles the default namespacedeferror_handler(e):pass@socketio.on_error('/chat')# handles the '/chat' namespacedeferror_handler_chat(e):pass@socketio.on_error_default# handles all namespaces without an explicit error handlerdefdefault_error_handler(e):pass
Error handler functions take the exception object as an argument.
The message and data arguments of the current request can also be inspectedwith therequest.event
variable, which is useful for error logging anddebugging outside the event handler:
fromflaskimportrequest@socketio.on("my error event")defon_my_event(data):raiseRuntimeError()@socketio.on_error_defaultdefdefault_error_handler(e):print(request.event["message"])# "my error event"print(request.event["args"])# (data,)
Debugging and Troubleshooting¶
To help you debug issues, the server can be configured to output logs to theterminal:
socketio=SocketIO(logger=True,engineio_logger=True)
Thelogger
argument controls logging related to the Socket.IO protocol,whileengineio_logger
controls logs that originate in the low-levelEngine.IO transport. These arguments can be set toTrue
to output logs tostderr
, or to an object compatible with Python’slogging
packagewhere the logs should be emitted to. A value ofFalse
disables logging.
Logging can help identify the cause of connection problems, 400 responses,bad performance and other issues.