Hi, this is Russell res, and
welcome to another video in the series fun with WebSockets. In
this video, I would like to teach you how to write a
WebSocket server in Python. So and the way we're going to do
this in this video is we're going to write a simple echo
server, which is going to send messages back to whoever
connects to the to the server. But we're also going to write a
broadcast echo server, which means a server that is going to
send an incoming message to all the
other clients except for the sender. So in a broadcasting
way, which is going to look something like this. So if you
see the terminal that I have here, I'm going to start running
the server, which I just started, and then I'm going to
run the first client, which is going to connect to the server,
but it didn't get any end it sent a message. And now the
second one is going to connect and the message that is coming
in sending this one is received by the server and is broadcasted
to the other client. And the third serve client that I'm
going to connect is going to connect is going to send a
message to the server, and that message is going to be
broadcasted to the first one, and to the second. Alright, so
I'm going to explain we're going to be using the code that we
have used in the previous videos for the client, but we're going
to be writing the server from scratch, okay. And I would also
like to thank salvadora, from the ParametricCamp community who
helped me put together this example. And hence, here we are
today. Okay, so thank you very much for that salvadore. And
before, before we start now, we can actually start, I think,
let's get hands on. And let's see how to write a web socket
server in Python. So let's start by refreshing
what we did in the previous video, when we wrote a Python
Client, the Python web socket client, if you remember, I had
on my desktop a in a folder called Python web sockets, I had
a client.pi file, which had this code. And this code was the code
that we were using as a WebSocket client to connect to a
to all these like ball chasing synchronization, stuff that had
been developed previously in the playlist. So what we have here
is that now I can run either from the terminal, I can open a
terminal here, or what we're going to do is we're actually
going to start using Visual Studio Code terminal. So what
I'm going to do is I'm going to go to the client to Visual
Studio, and I'm going to click on terminal, start new terminal,
and then you're going to see that all the way down there, I
have a my case, I have a PowerShell bash that I can use
to run commands here. So what I can do is I can go to the folder
where my client script is, and I can type here CD, and then I can
paste that folder there. And I can type dir and you can see
that I can see client.pi, right. And then if I now type Python,
and then client.pi, then you can see that I have, you can really
see because there's no but I have connected to the grid
server. And now if I click on, maybe I have to refresh this. So
now if I click on the ball chasing, you can see that it
synchronizes and that it sends messages to the WebSocket client
that I have connected here. Okay, so Alright, so this is
good. And then what we're going to do now is that instead of
using the client to connect to the gauge server that we have
developed in the previous videos, we're going to do
everything locally. So I'm going to just close this. And what I'm
going to do is I'm going to create a web socket server that
is going to be written in Python that is going to live on my
local machine. The way I'm going to do that is by opening, I'm
going to close this here, alright. And I'm going to type
CLS to clear the screen. And then what I'm going to do is I'm
going to create a new file, for example. So I'm going to create
a new new file. And then I'm going to save this file with
Ctrl S, I'm going to save it as for example server aiko.py. And
as I do that, I'm going to dock this side by side. So I'm going
to put this on the side here. And then I now have the two
scripts side by site, the client and the server. Alright, so I
think at this point, we are ready to start working on the
server. And then something that I'm also going to do for the
sake of development, which is very nice on Visual Studio code,
for example, is that I'm going to create two terminals. So here
I'm going to go to the terminal that I have, and I'm going to
split it and therefore now I have two terminals, one on the
right and one on the left. This one I'm also going to CD into
the folder that has my scripts Now you can take their or LS,
and you can see, like all the files that you have in your
system. And then because I will use this to run the server on
the right hand side, and I will use this terminal to run the
client on the left hand side. Okay, so I think at this point,
we're ready to start writing our server implementation. The way we're going to do that
is going to be simple. We're going to start by importing the
relevant libraries. So we're going to, we're going to just
import the same libraries as we did for the client, which is
going to be the web socket library. And we're also going to
import a sink i O, which is the library that allows us to do
asynchronous non imperative programming inside of Python. So
basically, the idea of like having a function that is
waiting, and that response, when we get particular, particular
events, our recent rise recent on, I don't know how to say
that. Anyway, I'm also going to define
a new variable that is going to be the port, which is nothing
that I didn't do before, which is going to be, for example,
7890, this is going to be the port that local connections need
to connect inside of my computer. So that's a new
variable that I'm creating. And then just for the sake of login,
I'm just going to see started the server. And it's listening
on port, for example. And then here, I'm going to type I'm going to work with this Yes,
so and then I'm going to use this string function to convert
this number into a string that can be connected to this to this
string here. Alright, so, um, so if I now just for the sake of
trying, if I now go here, and I type Python server, and I
started my server, you can see that I get this message, but
nothing happens, because it just executes and it finishes. Right?
And I have, sir, I just going to change this
server lists List listening on port bladder, alright. Okay, the next thing that I need to do
is I need to define a function, that is going to be the function
that is going to be running on the WebSocket server, so that
whenever a socket connects a WebSocket connects a client
connects, then that function is going to handle the behavior of
the server or for that client. And in this particular example,
I'm going to write right now, we're going to write a simple a
ko server, which means that whatever message comes from a
client, that message is going to be bounced back to that client.
So whatever the client sends to the server, the server is going
to send that back to the client as well. So how can we do that? Well,
we're going to define a, an a synchronous function, a function
that is going to be leaving and listening. And then we're going to define
it, and we're going to call it for example. And this function
is going to take two is going to take one is going to take, it's going to take two
arguments, which are going to be the WebSocket. And the path, the
WebSocket is going to be an instance of the WebSocket.
client. So whenever we need to refer to the client, we can use
this argument here. And but we're actually not going to be
using it. So if you want to if you don't want to use it, it's
actually also fine. And then we're just going to print some
acknowledgement message. So for example, when, because this
function is going to execute whenever there is a new
connection, so we can just right here, a client just connected.
Alright, just to make sure that we have some logs here on the
screen. And then here, what we need to do is now
we need to implement the logic of whenever a message comes from
the client, then send that message back to the client. So
what we can do is we can write what's called an a synchronous
for loop, which is whenever we get it looks something like
this. So whenever we find a message
that is coming from the WebSocket, what we need to do is
whatever we specify in this function, so the what we're
going to do is for example, I'm going to write a message. So
received message from I received a message from the client and
the message is going to be whatever message alright and then I'm going To send that same message back
to the client. So and I'm going to do that with an await
keyword, which is going to be web socket dot send, and then
the message back and we could actually send, we could actually
add something here like back or, for example, response, or Pong,
like when he says in ping pong, for example, whatever. Ah, what is going on? Exactly. Alright. Now, let me break this
down a little bit for you. So what is happening here is that,
while we need the asynchronous and the await, because the
WebSocket client may actually be sending several messages very
fast to the server. And then what we need to do here is we
need to write some code that makes sure that it takes one
message at a time, it deals with that message. And then it
doesn't start over again, until that message has been handled
has been dealt with. This is why we have here a for each, so that
we iterate over all the messages that might be coming very fast
to the server. But we do it in a synchronous way. So that the
next loop of this message doesn't happen until we know
that we have sent back the message to the client. I'm not sure if I did a good job
at explaining this. But basically, imagine situation
that the server receives 1000 messages very fast. What
this code does is it goes over each one of them one at a time.
And it reads that message, it sends back that message to the
client, and it'll make sure that it doesn't go and send the next
one, it doesn't deal with the next one until this sending back
to the client has been complete. Okay. I don't know if I did a good job
again, at explaining this, but I hope you trust me on that one.
Okay. All right. Now, and and remember
that whenever we have an await, we need to wrap it with an a
sync. And that's why we have these two clauses together here. Alrighty, so at this point, the
behavior of the server is ready. But we have not really
implemented anything that uses that behavior as part of a
WebSocket server. So that part we can do by using for example,
by using from the WebSocket server, we can use this surf function.
Alright. And then we can pass in the echo function that we have
designed. And then the second argument is going to be where
this server is going to be living. So that's going to be
localhost, which is a term that refers to this machine, I think
we saw that on previous videos, alright. And then the port that
we're going to be running this server on, that is going to be
whichever number we have defined here for this variable. Okay.
And what this function does is that it returns an instance of
that server, so that we can start, we can keep it inside of
a variable. So for example, I'm going to call this start server.
And then I'm going to, I'm going to, I'm going to keep the
whatever I'm getting back the instance of the new server,
I'm going to keep it active, I'm going to keep it inside of this
variable. Last but not least, we need to
actually start the server in a synchronous way. So the way
we're going to do that is from the sink IO library, we're going
to get the event loop. And then once we get the event,
we're going to run we're going to run on the it's
complete, we're going to run that instance of the start
server that we have defined. So we want to run that once. And
then we're going to make sure that the event loop after that
is running forever, so that the server stays alive so that the
server doesn't stop working. Okay, so, um, I think we are
ready to give this a try. The way we want to do that is in the
terminal that I have on the right hand side, I'm going to
start my server. So I'm going to say Python, and then server
Echo, it's very difficult to read. I don't know how to make
this terminal larger. So you can see that I get an acknowledgment
saying that the server is listening on port 7890 Which means that the server
should be fine at this point. Now, let's try connecting from
the client to the server. In order to do that, the first
thing that I need to do is I need to change the URL, because
the URL right now is pointing to the server that I put on glitch,
whereas this server is living on my machine. The address of my
machine is, as we discussed in previous videos, is this special
IP that is called 127 001. And then I with a colon, I need to
specify the port, which is 7890. All right, I'm going to save
this and then in my other terminal, so that I have the two
terminal side by side, I'm going to type Python and then client.
And what that's going to do is that it's going to start the
client, and it's going to immediately connect to this
server that I have running here in the other terminal. Let's
give that a try. So as I do that, you can see that I get a
message here that says that the client just connected, which
sounds like it's working well. But nothing happened. There was
like no messaging, no echo going back and forth, which is kind of
weird. So I'm going to stop the client by going to the other
terminal and hitting Ctrl. C, to stop the process. And then you
can see that I get a lot of like a lot of errors. And I also get
quite some errors here on the server. But the server has not
actually stopped. It just has just dumped like a bunch of
errors. So we will deal we will deal with that later. Okay. So
I'm going to clear all those errors by typing CLS. And then
what I'm going to do is that I'm going to change my clients so
that as soon as I connect to the server, I send a grid message
like hello server, how are we doing? The only thing that I
need to do is here inside of the a sink, connection to the
WebSocket server, what I need to do is I need to send a message,
but I need to send it with a weight so that I don't start
dealing with the following code until this has resolved
positively. So the way I'm going to do that is I'm going to say
I'm going to take the WebSocket instance. And I'm going to send
a message that is going to be for example, hello, server,
something like that. Okay, I went to save that. And then I am
going to hit the arrow key up until I find again, Python
Client. And then I'm going to run this again. And can you see
what just happened. So the server the client connected to
the server, then I got a message here saying that the client is
connected. And then I got another message saying that I
received a message from the client, hello server. And
because I sent the message back with this, you can see that in
the client, now I am receiving the message that is called bond,
which is Hello server. Hooray. So it looks like this is
working. The client connected the server, it sent a message
the server received the message. And it punked it back to the
client. Yahoo. So we have the basics working here. How can we
make this a little cooler better? And like avoid all these
like clutter of error messages? How can we do that? The first thing we're going to
do is we're going to improve the server a little bit so that it
doesn't dump all these messages when a client disconnects.
Alright, so what I'm going to do is I'm going to stop the client
with Ctrl C, I'm also going to stop the server with Ctrl C, and
I'm going to clear all the clutter CLS and CLS. Alright,
right. And then the, the idea is that the error
messages from the server are a cost because when the client
disconnects, there is no WebSocket that we can cycle
through and that we can send messages back to you. So
basically, this code here fails, because the WebSocket is not
present anymore. So when we want to deal with errors, or with
exceptions in computer code, we typically use try catch
structures which are also present in which are also
present in, in, in Python. So the way we're going to do that
is that we're going to use try. And then we're going to wrap all
this code, the code that we think can be faulted into a try
class. And then what we need to do is we need to handle the
exceptions that might arise from this from this code here. So we
could just say like, we could just say it There is an exception, then
displaying the message like something, something went wrong,
something like that, for example, and let's give that a
try. So I'm going to run the server, I'm going to run the
client, everything goes well. But then when I stopped the
client, you see that now instead of the whole dump, what I get is
too simple, like something went wrong. Okay. But but that is
because the only thing that we have done is we have add a
general handler for any exception that might happen. In
this case, because we know that we want to be able to handle
precisely the case of a client disconnecting, then there is
particular exceptions that we can target. Because when an
exception is raised, is typically raised with a
particular signature. What that means is that, for example, in
the case of the WebSocket library, we can see we can go to
whenever in the web socket exceptions, we find a an
exception that is connection closed. Alright, so if that is the
exception that we have found this, give it a name, for
example, it's going to be E. And then here, the message is going
to be different is going to be a client, just Oops, sorry for
that client just disconnected. And then we're going to print
the error message, just to make sure that we see it. So I'm
going to close this. And then I'm going to CLS CLS, and I'm
going to start the server again, I'm going to start the client
again. I'm going to disconnect the client and you can see now
that the message is a client just disconnected. And the code is the connection
was abnormally close for no reason, whenever that means,
okay. All right. So that's looks much better, because now we can
connect and disconnect connect and disconnect. And things are
just are much better handled now. And so I'm just going to remove
printing the error message. And I think this is going to be good
for handling the the error of the disconnection. So I'm going
to close my server, I'm going to close my client and everything
various is connected. All right, and I think we are ready now for
a bit more of enhanced server behavior. What are we going to
do next? I think what I would like to do
now is to implement a broadcasting echo server, what
that means is that it's still going to be an echo server is
still going to be bounce, the is still going to bounce messages from the clients back
to the clients. But the only thing that we're going to change
is that if we have multiple clients connected, then whatever
message is received by one client is going to be sent to
the rest of the client, but except for the one that sent the
message, so that whoever sends the message is not receiving the
same message back. So how are we going to do that? Well, first of
all, I'm going to save a new file. So I'm going to go to
File, Save As, and I'm going to right here, server echo
broadcast. Alright, and then the next thing that I'm going to do
is, what I need to do is whenever the first thing that I
need to do is I need to keep track of how many clients are
connected to my server. So what I can do for that is I can
create an array or I can create a list or whatever. But in this
case, we're going to use a set as a data structure so that we
are so that we can be sure that there's going to be no
duplicates in that in that in that list. So what I'm going to
do is I'm going to create a new variable that is going to be
connected, for example. And then here, I'm going to create a new
set. Alright. And then what we're going to do is whenever a
client connects, whenever we're playing Connect, we're going to
add that client to the set. So we're going to say connected dot
add, and we're going to add an instance of the WebSocket that
just connected. So this list, this set is going to keep a copy
of all the web sockets that are currently connected to my to my
server. And then what we're going to do is we're going to, we're going to whenever we
receive a message from the WebSocket where we're going to
do is we're going to keep printing this message to the
console, but instead of sending the response to the That same WebSocket, we're going
to do is we're going to iterate over all the sockets that are in
the connected list. So for example, we're going to say, for
connected in connected. So for each one of the sockets
that is in the list of connected sockets, we're going to do is,
if that one, the one that we just chose is different than the
one that sent the message, which is WebSocket. Because we are in
this function, then if it is different, then a way to
connect, let's send a message. For example, for someone said, and then the message Okay, and the rest is going to stay
the same. Correct? Yes. So let's give this a try. So I'm going to
write five, and then server Aiko broadcast. So now, I'm going to
start the server. And then here, I'm going to create a new
client. So you can see that this looks like it's starting to work
because the client did send a message Hello server. And we
know that because the server actually locked that. The
problem is that no one else received that message, because
there's no more clients here. So what we're going to do is, we're
going to go here to this terminal. And we're going to
click here on the tiny icon that says split screen, and I'm going
to create a new, I'm going to create a new terminal, and I'm
going to create another terminal and another terminal. So I'm
going to create five terminals. And I don't know if you can look
at maybe I don't need five, maybe three is enough. But maybe you can see here, I
still have the server on this one. All right, but I have two
other sermon, two other terminals. So I'm going to copy
the route of where everything is see the CV and I'm going to paste the
row of CLS, and then C V, I'm going to paste the route and CLS
to clear the screen. Alright, so now I have these two other
terminals that are on the same folder. So now what I can do
here is pipeline, and I can start the client. And then as I
do that, what should happen is we should see here messages for the
connection. And then this terminal here should receive the
message that this client is sending to the server. So let's
see that. So I'm going to click that. You saw here, a client
just connected, I received a message Hello server. And then
this client, the one that was previously connected, received,
someone said, Hello server. So the message that this client
sent to this server was bounced to this other client. And then
we're going to try with the third client as well. So I'm
going to type Python. And I'm going to say client, and then
what we should expect is the same message here, but these two
other clients receiving this message. So let's try that. So I
did that. We got the acknowledgement. And then we got
the message for this client. And we got the message for this
client as well. So Yoo hoo. So this is working. It looks like we have, it looks like this is working.
But I think if I close this up, and I now connect another
server, again, we are going to get errors anyway. Something that I would like to
do is I would like to make sure that if we have an error because
the connection is closed, I would like to make sure that I
from the connected list, I would like to remove whichever
WebSocket has been has has whichever WebSocket has left the
party. Alright, so that's why I'm going to add here to the
try, I'm going to add Finally, which is going to be a part of
the try catch clause that is always going to execute no
matter the exception. And we're going to make sure that we
remove that WebSocket from the list of connected sites. Okay,
this is just for making sure that things are clean and well
written. Alright. Okay, so I think you can really
see this. So I think this is what I wanted to cover in this
video. This is a way of making a simple WebSocket server using
Python. We have written a simple echo server, more complex echo
broadcast, but now you can see how you can just add more
behaviors you can take the message that is coming in and
you can use that message to changed the behavior of the
server to CIT to send all the data to the clients to connect
to operations, whatever the sky is
your limit now with this, okay? All right, wonderful. So, thank
you very much for watching this video. If you liked the content,
maybe consider liking this video or even subscribing to the
channel, hit the notifications on and all those things that
makes that help us send the message across. And in
the meantime, maybe you want to consider continue with this
playlist and see what other things we have that we can do
fun that we can have fun with using WebSockets See you in the
next video. Bye