C++ Qt 68 - QTcpServer using multiple threads

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
everybody its Brian and welcome to the 68th cute tutorial with C++ and GUI programming today we are going to make another server this time we're going to make a multi-threaded server so we'll call this multi server put it in a usual place if you watch the last tutorial you probably notice that it only did pretty much one connection at a time which is pretty weak so we want to handle multiple connections at a time so first things first let's add in our network module then we need to make a couple classes here so the first one we're going to make is going to be the server we're going to call it my server and the base class for this is going to be aq TCP server here it's Q object make sure you spelled that right when you do that I made that mistake one time it's actually kind of difficult to debug alright the other class you want to make we'll call my threat and it is going to have a base class of cue thread hair it's Q object once again make sure you spell that correctly alright and then let's just uh let's compile this at this point just to make sure that we spelled everything correctly we don't have any boo-boos all right we've got a good clean build let's continue so first thing we want to do here is we want to open up my thread this is going to be a thread that your connections going to run in and what you really need to understand about this is that every time you have a new connection a new thread is going to be created and we're going to discuss a little bit about that at the end so because this is actually a thread one thing we need to do is actually have the run function in there so we'll say void run and then we need to add in our signals and slots so say public slots and of course we want the slots of the socket which are you know the ready read and disconnected but we're going to just go to Q TCP ah we didn't add in our include sorry about that let's go ahead and add in our concludes before I forget again Q TCP socket and cutie bug just so we have those in there alright now we can go queue TCP I hope if I could spell that right queue TCP socket we're just going to call this socket and we also need the socket descriptor don't really need it but you're going to see what's going on here in a minute let me just take this out the socket descriptor is the underlying socket ID number from the operating system and we're going to actually want to modify our constructor here a little bit so we'll say int ID and then of course we need to do that over here and then we need to implement our run and that's where this socket is going to start when it does it's a multi-threaded function so we're just gonna say thread starts here okay go back in here now we need those slots so what we're going to do is just look up Q TCP socket I'm doing this just in case you didn't watch the last tutorial you notice how there's really not much in here it's because it inherits pretty much everything so it's from the QIO device and we want the the signals here we go sorry about that we want the ready read tells us that there is information available well I didn't work out well having nothing but problems today here we go copy this Oh still didn't copy it well you know I'm just going to type it in then void ready read obviously I've got my keyboard still set up for gaming and we want disconnected that way it's not going to conflict with the thread disconnect so those are our slots here in our socket when we create an instance of this is going to just emit our signal and these slots are going to consume them very simple class we have here but now we need to implement it so first things first we need to say this socket descriptor equal Heidi that way we know the underlying socket ID number now we could at this point actually create the socket and set the socket descriptor right there but I just want you to understand that there's a very big difference between the socket descriptor and the socket itself okay now what we're going to do here is we're going to actually going to add in a signal sorry I'm kind of scatterbrained today and we're going to say void error that way if something goes wrong we understand something went wrong queue TCP socket we're going to say socket error will call this socket error just in case um I don't really mind messages I just don't like it when I get like a thousand messages saying hey how come this won't work and then I'm trying to figure out how your internal network functions all right so now our headers pretty much complete here now in our run remember this is when the thread actually starts we haven't connected the socket yet but this is when the thread starts so what we need to do here is just a cute bug just so we know that something's happening it will say starting thread yeah kind of messed up my quotes there didn't I that's um you're probably giggling my horrible typing anyways that way we know that we're actually starting the threat and we're gonna say socket equal new queue TCP socket make this the parrot actually no I forgot don't make this the prayer you're going to start having all sorts of issues if you do that I had some testing and it wasn't very good at all so socket and then we'll say set socket descriptor and this is where you set the ID you can just set the socket descriptor there and so we're saying if not in other words if something bad happened then we're going to just omit our signal and we're going to say hey there was an error something bad happened and if you've looked at the if you go into here and choose examples and then go into where's networking way up at the top if you go into the fortune server and then there's a threaded fortune server you'll notice this is very similar to that okay now we need to connect our signals and slots up now one thing you should know is that we're going to do this a little bit differently so we're going to say connect socket signal already read and we're going to connect it to this slot already read and this is where the different part comes in we need to treat this a little bit differently because it is multi-threaded so we're going to say cute direct connection and what that does is it makes a direct connection to that thread otherwise you're going to start having all sorts of cross thread operation errors and then disconnected connect up our other slot here there we go now we've got our signals and slots all connected and ready to go and then we just say queued bug we want to know that the socket connected so we're going to say mm let's see here we want to know which one it is so let's say socket descriptor because we're going to have multiple of these right so we want to know which one's actually doing what and I know this might look really confusing and we're going to go over this so don't worry just try and get the code out of the way I like it connected now of course we need to implement already read and disconnected so we'll say avoid my thread ready read remember this is when we're actually got data in the pipe we're ready to go and then void my thread disconnect it and this is when the client socket is disconnected so we need to treat these very differently obviously now what we want to do is basically whoops sorry I just nailed the microphone what we need to do is make what we're going to call an echo server echo server used to be really popular back in the day you rarely see them anymore and what it is is you send information to it and it sends you the exact same information back it was a very handy troubleshooting tools that some administrators used a long time ago so we're going to say Q byte array and we're just going to call this data equal socket read all so now we get the information and we want to know that they actually sent us data so we're just going to say data in that way we can see on the server window what was actually sad now we're just going to write that back out so let's say socket right and we're just going to write data out and that's all there is to that now one thing I should probably back up here one thing you should know is we are missing a critical step here that I want to discuss in my thread run if you know anything about threading when it goes through this run command it'll hit the end of this and stop so if you haven't noticed it already we have a huge flaw meaning it's going to just drop that threat and destroy it so what we need to do is called exec what exact does is it creates a message loop meaning this thread will stay alive indefinitely until we tell it to close now in the disconnected the first thing we need to do is exit and give it an exit code so we're telling the thread at this point ok we no longer need you go away that's how we can do the signals and slots if you omit exec like if you just don't have it in your code your code is going to run get to this point and the threads immediately just going to drop and the sock is just going to sit out there in never-never land and you'll get some pretty unpredictable results at that point so in disconnected we need to do two things we need to a get rid of that socket because it's still in memory so we'll say socket delete later and what that does is it flags it for deletion so after the execution context leaves this function cube will automatically delete that object for us and then we want to tell the thread to exit now if you're wondering why we don't just make socket parent as this it's because you get some real funky cross thread operations or at least I did let me know if you have different results but I had a really hard time getting that to work then we just want to know which client is connected let's just go ahead and compile this real quick data was not too clear in the scope ah yes see that's why we that's why we do a very nice little compile here just so we miss all that fun all right let's compile it one more time not a signal okay let me pause the video and see what I did here mmm I can finish probably slow down when I make these things silly me all right there we go should get a clean compile out of this now there we go sorry about that it's Friday it's been a long day I'm really trying to get this video out before for the pizza gets here all right now we've got our server which we haven't done anything with so what's the big deal with the server you see how it inherits queue TCP server well we have to do two things we have to start the server and we have to handle incoming connections and that's really the whole job of the server here so in the public section we're just going to say void start server and then we're going to make a protected area if you remember from our our inheritance concepts void and we want to do incoming connection and it's actually socket descriptor not that it really matters much but you get the point so we have our incoming connection and start server so let's just save a bit of time here by copying that and my server we just need to implement this really quickly and this is where we're going to actually start the server and then we're going to just do this and this is where we're going to handle incoming connections as you probably put that in the proper namespace will have all sorts of weirdness going on here all right so let's go ahead and start the server here we'll say if not this listen and this is old-school stuff you know we did this last tutorial you know exactly what to expect here we're going to set it to any and we're just going to set port one two three four just forget you can set any port you want and then we want to do a cue debug and we want to know that we could not start server or we want to know that the server is currently listening so sikita bug sorry about that my fingers happen to go faster in my brain will just say listening so very simply we just start the server and then on the incoming connection this is where the magic happens and we need to add reference here so let's go back into my server and we need to actually include cutie bug and let's go ahead and include my thread in the back and server land here we need to actually say we have a new connection here so it's acute a bug that way we know that that guy is connecting and then we want to say my thread we'll just call this thread equal new my thread and we won't course want the ID so we need to give the socket descriptor and we'll give it a parent and this is a adequate parent so we'll add this class is the parent now we need to connect up the signals and slots um we haven't actually added those in yet but what we want to do is we want to know when that threads finished we want to delete it later that way we don't have to mess around with this and it sounds confusing but it's very simple we just do connect thread signal whoops sorry finished so we want to know when that threats actually finished and then we're going to put the slot back on itself and we're going to say delete later so what that does is it thread Thrax it Flags that thread for deletion so once it's no longer used q will automatically delete it for so we don't have to worry about it and then of course we just need to start our thread which calls our run function we can set the priority if we want but we're not going to really mess around with that too much all right now in main dot cpp we need to of course add in the server and start it up and we want to start server there we go now let's go ahead and run this see if we actually get a good compile and I've been kind of whipping through this made a few mistakes there's listening and let me go ahead and open a command prompt here and do telnet open one two seven zero zero one one two three four and you can see how we have a connection and when we type data you can see that it's echoing the data back I press the M key once and we get two M's that's because this has a local echo so we're seeing what we typed in what we got back from the server and if we really wanted to be nice we can actually say you know data from server e equals blah blah blah but I'm just going to open up another one of these I mean the whole concept of this tutorial is to show you that we can handle multiple connections at the same time so I say telnet open one two 7.00 one one two three four and you see now we have two clients connected you see we have two ninety two and three forty eight this is two ninety-two this is 348 now how would you tell those apart very simple you could go into the actual socket and get the remote host and the remote IP address in the remote port that's how you tell those apart the whole concept here is that you can see we have multiple connections at the same time and when we close one you see 348 disconnected this guys stay live and we can actually make another connection and let's just go telnet open one two seven zero zero one you wonder what that is because you haven't watched my previous tutorials that's the local loopback interface all all modern computers have that you can see the socket ID is 380 now if we kill the server you'll see both of these disconnect at the same time and see connection house lost connection I've lost pretty neat stuff all right now I believe a very good review is in order I'm going to try and go through this rather quickly but very informative Lee at the same time because my dinner is coming all right now in main.cpp we have and include to my server we're just saying server start server that's the function we made so when we go into server you can see yes there is our start server and we're handling the incoming connection with the underlying socket descriptor which is just a number from the operating system and you can see in the implementation start server all we're doing is starting the server we're saying if we listen yes we listen otherwise we can't incoming connection we're creating a new instance of our thread and we are actually flagging that thread for delete later when it's finished and then we're just saying start when we call start it instantly jumps down into the run function and execute this code but when it was created we grab the socket descriptor that way we know what number it is and you should note that we could very easily have created the socket and set the descriptor right here but I wanted you to understand that the socket descriptor and the socket itself are two different things the socket already exists the sockets already connected I should say it's in a pending State as far as the operating system is concerned as far as our codes concerned we haven't created that socket so it doesn't exist that's the difference between the socket descriptor and the socket itself and then we connect up the signals and slots you can see the ready read so we know what we have data ready and the disconnected so we know when the client is connected and then of course here's our ready reef we're just getting a cube I'd array with all the data and shoving that back out into the socket just echoing the data back out there and you could send anything you want and then disconnected we're just saying okay socket delete later that way it doesn't stay out in memory and then we're going to force the thread to exit actually it's not true we're not really forcing the thread to do anything we're just telling the thread okay stop this message loop that we set up because without this message loop you're going to get very very unpredictable results I think on most operating systems it'll go through this code and then just stop the thread will actually just die because it has nothing else to do but because we have this message loop it's going to sit there and sit there and sit there until we tell it to go away so that is multi-threading in a nutshell now there's some potential pitfalls with this application I should tell you that 90 95 % of all TCP server applications are written just like this they do a little special magic like they make a cue list and they put the sockets in it so they know you know what are connected they can enumerate throw them and can disconnect or send messages to specific clients but the underlying mechanism is almost identical to what we've just done they make a socket and they make a thread per socket the pitfall to that is threads are very expensive resources on an operating system so let's go cue thread and if you read through this you'll see and I think they actually mention it in here somewhere that yes cue thread represents a separate thread of control within a program shares data with all other threads and processing an independently bubble a little blah basically a thread is an expensive operation you will run out of threads so if you're having a server and you're running in 3,000 people connect you I don't know many computers they're going to run 3000 threads per app it's probably going to just crash and die and then you're going to be scratching agaln will it work perfectly why is it suddenly doing that so there's a better way of doing this and you should look up the cue thread pool and what a thread pool is it's a collection of threads where you can have say 2 3 5 10 threads and those threads alternate and do a lot of work that's how web servers like iis or patchy work is they have just an ungodly amount of resources behind them but they use a thread pool and what that does is you have two or three threads that do the work of hundreds what do they do is they read the socket and if the socket doesn't need to do anything they just shove it off in the background now the thread pool works by the cue runnable class very similar to a thread but it's kind of a fire aren't forget sort of mechanism see it's got the run and that's where your execution will jump in so you'll implement a cue runnable class and basically do your code within the run function now I'll admit that I kind of played around with this a little bit but I don't have a full working example and what I'm really hoping to get for you guys is a full working example using cue runnable here very soon but almost out of time getting hungry and I'm sure my food's almost here but I wanted you to know the potential pitfalls of using threading so if you're going to use a purely threaded interface which is fine there's nothing wrong with it just make sure you set a max connections otherwise you will run out of resources if you get too many connections so this is Brian I hope you found this tutorial a tional and entertaining and thank you for watching
Info
Channel: VoidRealms
Views: 64,208
Rating: undefined out of 5
Keywords: Qt, 68, QTcpServer, using, multiple, threads
Id: iKtCXUHsV70
Channel Id: undefined
Length: 27min 13sec (1633 seconds)
Published: Fri Apr 15 2011
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.