Recently I started making web applications using python. Given I am so unfamiliar with the frameworks available and that I’ve always wanted to learn how to do realtime applications with socket.io, I thought was a good idea to try to make a chat with flask, socket.io and angular as an exercise.
So socket.io is a library that enables event-based communication between clients and a server. For this chat application, I thought the simplest thing would be to handle three different events:
- A user connect
- A user disconnects
- A user sends a message
To handle the first two events, this is how my code looked like:
While working on this, I struggled with Cross-Origin Resource Sharing (CORS) until I realized that I needed to add the
SocketIO(app, cors_allowed_origins="*") in addition to
@socketio.on('sign_in') will get triggered when someone signs in. The client will send an object with the property “name” and the request will include the “socket id” in
request.sid . I thought the easiest way to track the signed in users was to store them in a dictionary where the socket.id will be the key and the user name the value
user[request.sid]=user_name['name'] . Once someone signs in, then I just emit the event “current_users” and send the users dictionary to all the clients.
@socketio.on('disconnect') will get triggered when someone disconnects and will delete delete the user from the users dictionary with
users.pop(request.sid,'No user found') (the “No user found” is just a convenience so this doesn’t throw an error if request.sid doesn’t exist in the dictionary). Once the users dictionary is updated, we will emit the “current_users” event again with the update list to all clients.
Now that we have a system to track users, lets handle receiving and sending messages:
The above code will receive and emit messages. Here, I am leveraging the fact that each client joins a socket.io room with their respective socket ids by default . Here I am emitting to two rooms, the room from the sender and the room from the receiver. Basically that means that a client will be receiving all the messages through the same room (that has its socket id), so I will let the front-end to handle how messages received from different users are displayed (I used a filter pipe in the front-end).
But that is everything needed for the back-end. The full code looks like this:
The Angular front-end is a bit convoluted to be described here. But briefly, I am using two services: The LoginService (
login.service.ts ) and the ChatService (
chat.service.ts ) . The LoginService handles the sign_in event:
login() function will emit the ‘sign_in’ event and send the user details to the server. The server will then add this new user to the users dictionary and let all the clients know about this new user by emitting the ‘current_users’ event with the update users dictionary.
Finally, the ChatService handles the rest of the events for the chat:
getUsers() function returns an observable which I subscribe to keep track of the connected users. Similarly,
getMessage() returns an observable to keep track of all the messages sent to the client. The
sendMessage() function will emit the ‘message’ event and send along the message which is a JSON object with details of which client this message should be sent to.
The final product, is a very simple chat application and a very good learning experience for me:
The full code is here.