8.2 How to Create a WebSocket Server in Python - Fun with WebSockets

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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
Info
Channel: ParametricCamp
Views: 5,899
Rating: undefined out of 5
Keywords: Rhino, p5.js, Computational Design, WebSocket, Introduction, Grasshopper3D, Parametric, Code, Computer Programming, Glitch.com, Computational, C#, Geometry, Python, Designers, Unity, JavaScript, JS, Algorithm, VVVV, WebSockets, Nodejs, Rhinoceros, Learning, Node.js, socket.io, Processing, Grasshopper, Visual Studio, Design, Visual Studio Code, Glitch
Id: SfQd1FdcTlI
Channel Id: undefined
Length: 30min 46sec (1846 seconds)
Published: Sat Jun 26 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.