 Hey everybody it's Brian and welcome to the 70th Qt tutorial with C++ and GUI programming. Today we're going to be doing an asynchronous TCP server. So we'll call this ASVR, I'm going to just call it 2 because I've already done a few of these in testing. I wanted to make sure I had a working model. So as a result you might see me doing a little bit of copy and paste magic but I won't do anything too extravagant. I want you to be able to keep up and see what I'm actually doing here. So this is all stuff you've seen before just adding the networking module. I'm adding a new class, we'll call it MyServer and MyServer of course if you've been following along in our other tutorials in here it's Qtcpserver and we're just going to start filling this thing out real quick. So first thing we need to do is we need to of course add in some includes and includes take me a while so I'm just going to copy and paste. We're going to add in Qtcpserver because we need it Qtcpsocket and Qabstraksocket. We're doing those for the signals and slot mechanisms. All right now we want our void start server and that's where our server is going to be starting. Whoops, hope if I can spell void incoming connection and that's where we're going to handle our incoming connection for our server. So let's go ahead and just copy these, jump right into our server code here. Now I've uploaded so many videos that I don't really know what my time limit on YouTube is. I know it used to be like what 10, 15 minutes and it's been getting bumped up and up and up but this is going to be a fairly complex tutorial so like I said I'm going to be doing some copying and pasting. But you've seen like this code for example a million times we're just saying if the server listen on Qhost any address port and we'll just say 1, 2, 3, 4 then start it otherwise not start it. You know you've seen this if you've been following along in the tutorials and now we need to actually stop at this point and make a client. So we're going to say new class and our client is going to be anybody, anybody it's going to be a Q object. So we'll call it my client, here it's Q object, next, next. That way you know we just want a very simple wrapper for a socket because we want to be able to capture the signals and slots. Part of sockets is there is synchronous and what does a synchronous mean? Well it means that it happens outside of what you see. If you're synchronized you are you know doing something in parallel with someone. Asynchronous means of course it does something without you. Alright so we're going to make a simple function called void setSocket and I'm just going to say int descriptor and then we need our public slots. Oops I already had public slots that was a waste of time, sorry about that. And I'm going to make a few slots here and I'm just going to copy and paste these out. We're going to do the connected for the socket connection, disconnected for the socket disconnection, ready read so when we have data from the socket and task result. This is something we're going to cover here in a little bit. What we're actually going to do is when the client connects and then they send us some data we're going to do a task using the thread pool. That way our socket communications descend and receive very synchronous meaning they work on another thread we don't have to worry about them but then it jumps back into our main thread and we want some sort of time consuming task that we want on another thread and shoot it back up to the client. That way our server is fully asynchronous. Alright you'll see what I mean as we go along. And of course we need to private, forgot our headers here and I'm just going to add these in real quick and I'll explain them as we go. Queue object, queue TCP socket, queue debug for obvious reason and queue thread pool. This is where we're going to be using our threading. I don't necessarily like copying, pasting code because I feel it kind of cheapens it a little but this is going to be a fairly advanced topic and I want to make sure that you don't spend time watching me type as much as you spend time learning this. This is one of the techniques that is actually used by the pros. So it's a fairly good idea to really nail this down. Alright now we need to fill out our client code here. And what we need to do first is set the socket so we'll say void. Set my client, set socket. And this is where we're going to set the socket descriptor of the client. And I'm going to of course copy and paste the connection code just because it takes a while to type that out. And you've seen this before. This is just connecting the signals and slots together. We're just connecting the connected, disconnected and ready read with slots that aren't implemented yet but we're going to get there real quick. So we'll say void, my client, connected. And then of course we're just going to copy and paste this just for sake of speed here. I will admit I really, this is like the third or fourth time I've done this video, I've really tried to shave a lot of time off this and it's becoming very difficult because this is an advanced topic. All right ready read and that's why I'm doing a lot of copy and pasting. Okay now, what we need to do up here in the set socket, this is going to be called by the server, is we're going to say socket, yeah, equal new, qtcp, socket, this. Let's actually move this above our connection code, that way we actually have an object to connect with. And then socket, you know of course that socket descriptor and we're just going to say descriptor. That way we get that underlying socket and then of course qtabug just so we know what's going on here. We'll say, connected, that way we know the actual client connects. Now why aren't we doing it here? Why aren't we doing it in this event of client connected? Well you'll see this actually never gets fired off, you can say client connected event, this really never gets fired off because the socket's already connected at the TCP server. We're just binding to that socket. That's why I wanted to add that just to show you that it's really not there and then of course client disconnected. And then ready read, we're just going to do of course a qtabug socket read all. All right, now that we've got that filled out, let's jump back over into our server here and we need to handle this incoming connection. So what happens when of course we have a connection? Well, we need to add our include here, myClient, go back into our server and say, myClient, we'll just call this client equal new, myClient. We'll just say this is the parent. When the server's destroyed, all the other little objects get wiped out as well. And then we're going to call setSocket, and this is actually just connects. The underlying socket descriptor on the incoming connection. Very, very simple here. Let's actually do a test build. We got some issues here. Listen not to clear the scope. Oh, it's because I forgot the namespace. My bad. I had a penny for every time I did that. All right, now let's do a test build. Invalid use of this. Boy, now you see why these videos take a while. All right, now we should. No, guess not. MyClientClient, oh yes. See, this is what happens when we rush through things. Oh, good lord. Yes, because it's a pointer. Sorry about that. Little embarrassing little issues in my code here. OK, task result. Let me pause the video real quick. Silly me, undefined reference too. How did I catch that? Let's go back into our client here. We just never implemented it. So let's go void, MyClient, task result. Now, if you're wondering what task result is, we're going to perform a time-consuming task on another thread and throw it back into the client. Let's see if we can get this thing to actually build and run. Here we go. There's a build, but we haven't run it yet, of course, because we had to do this. Include, MyServer, say server, and server, of course, startServer. Maybe. There we go. IntelliSense didn't like me today. So of course, we will start our server, and let's just fire open a telnet just so we can see that this thing is actually running. Whoops, forgot the port. So we can see, as we type, we get information. So our TCP server is running. Now, you should notice that when you hit buttons on the keyboard, that data is being sent to the server. Let me actually move this over so you can see what's going on here. This is all happening synchronously, meaning none of that information is being broadcast on your main thread. What happens is a Qt has an underlying thread that it uses. Uses that thread, pops it back up into your main thread using the event system. So you have a fully asynchronous server, and you have not even touched threading yet. But there's an inherent problem here. Let me show you what this problem is. And that's the whole topic of this conversation. That's why I was flipping through this as fast as I could. We have a client server. The server, of course, when we get an incoming connection, makes a new client. The client does set socket, and we connect our signals and slots, set the socket and stripper to actually get the underlying socket, and then we're connected. And then we have all our information here, like ready read. We're just reading what the client sent us. The problem is, this right here, ready read. This is happening on our main thread, not underlying asynchronous thread that Qt was using. So if we want to do a time consuming task, that'll lock up our main thread. So any other clients that are running are going to hit this same point in the code, and they're going to lock up as well. So if you deadlock this thread, and let's just say deadlock right here, meaning the whole thread just comes to a screeching halt because you're trying to load a 50 gig file, all your other sockets are going to hit the same spot and just deadlock. Now, you could argue, well, that's why you use mutexes and semaphores and things of that nature, but you know what? There's a much, much, much easier way, and that's what I'm going to show you today. And I'm sorry it took this long just to get to that point, but like I said, this is an advanced topic. If we go into our client here, you see we're using the thread pool. Now, one thing we haven't implemented is the actual thread pool itself. Why is that? Why haven't we done anything with the thread pool? Because I'm going to show you that we use the actual global instance of the thread pool. See, every application when it's created in Q gets a thread pool by default. So you don't really need to create one and mess around with it. So what we're going to do is we're going to make a new runnable. And this one is going to be called My Task. And this is going to be our very time-consuming task here. So let's just go New, Class. And we're going to have to perform a little bit of operation on this just to get this to work with signals and slots. This is kind of a tricky one. So we'll say My Task, Base Class, and we'll say Q Runnable. Is the base class? Does not inherit Q Object. And now we have our task here. And let's look at the header of this. Notice there's some things missing. We don't have the Q Object macro, so we can't really use signals and slots. And we need to be able to use signals and slots. So we need to add in a few things. So what we're going to say here, and let's actually add in our includes first. Let me just copy and paste some includes for my notes. We're going to use Q to bug, Q Object, and Q Runnable. Now, Q Runnable we need because this is actually a Q Runnable. We're going to use this through a thread pool, which we've covered before. But we need Q Object because we want to use signals and slots. You notice by default, you don't really get signals and slots in a Q Runnable. So we need to perform a little bit of operation here. And those of you that have been with me from the start, you remember the inheritance tutorial where I said, well, rarely do you actually use multiple inheritance. This is one of those rare times where you actually do. We're inheriting both Q Object and Q Runnable. And now, of course, we need the Q Object macro to actually turn this into a full-fledged Q Object so we can use signals and slots. Now, of course, we want to implement protected. And we're going to say, we don't implement protected. Sorry, we just want to implement the run. That way, we have our full-fledged Q Runnable here. But we also want a signal because we want to be able to save when this is done. We want to notify our main thread that, hey, we're done. And we're going to say result. And we're just going to say int number because we're going to generate a number. And we want to know what that number is. So this is a very simple Q Runnable. It just inherits Q Object and Q Runnable and implements the Q Object macro. We have our run and a signal of result. So let's go ahead and jump into myTaskCPP. Deliver copy and paste magic, save some time. Whoops. And this is where the bulk of our work is going to be. And we're just going to make some sort of time consumer. And what we're going to say is just Q debug, task started. That way we know that our time consuming task started. And this isn't going to be really time consuming. I just want to show you that we're doing something. And we're just going to say for int i equals 0, i less than, we'll say 1,000. Actually, let's just do 100 just for giggles, i plus plus. And you can do any sort of time consuming task you want. We're just going to say i number plus equal i. We just want to know what happens if we just keep adding this number up over and over and over and over again. And then we're going to say Q debug. Now, you could argue that this is not a very time consuming task. And you're probably right. If we wanted a really time consuming task, we could, I don't know, make a group of rainbow tables. It takes a few months to implement. But that's being facetious. Really, we just access a big file or something like that. But what we want to do is just show you that this is just going to work. And we won't have very many headaches with it. And we're just going to say i number. So when the thread pool kicks up and it implements our Q runable, it's going to hit this run. And it's going to do this time consuming task. And we're just going to say, do something that's going to take some time. And then when we're done, we want to kick the result back up to our main thread. That's why this is a little bit tricky. Because remember, this is running in a separate thread. And we don't create this thread. And we don't control this thread. Qt does. We actually have very little control over this at all. All we do is hand it off to the thread pool. The thread pool fires it off on one of the Qt up threads that's sitting there ready to go. And remember, this may sit in a queue for a few milliseconds, a few seconds, a few minutes, depending on how busy the server is. All right. Now that we have got our runable there, we want to go back into our client. And you notice we have our task result. This is what's going to happen here. When we have our read, we want to be able to perform some sort of action. So what we want to do here is we want to go back into our header. And we're going to say, include my task. So when we read something, we're going to do our time consumer. And what we're going to say is my task. And we're going to make this, obviously, a pointer. My task equal new. My task. And we're going to say, in my task, set auto delete, because we want Qt to delete this. So we don't have to worry about a bunch of pointers and gobbling up our memory. Now we want to connect the signals and slots. And this part is going to take a little bit of explanation, but we'll go through it fairly quickly. Connect my task signal result. See, there's where we're popping back our result. And we're going to say this, slot, task result. Now this is the part that needs a little bit explanation. You notice how at the very end it says, I don't know if you can see it because the video is probably chopping it off, but it wants to know a connection type. And in the past, we've used Qt Direct Connection. Let me move this over so you can see it. Well, if you look up in the documentation, there's different ways of connecting. And what Direct Connection does is it fires it off immediately. Well, the problem with that is we're on a totally separate thread. And that's not really a great idea. So what we're going to do is we're going to do what's called a Qt Connection. And if you look up the documentation on a Qt Connection, I just copied and pasted it for you, a Qt Connection will fire off the event or the signal, sorry. And it'll sit in a queue until the context of execution rejoins the calling thread, meaning it'll pop back right here. So in the context of execution, rejoins our main thread, this is where it'll fire it off. Pretty simple. Now, what we want to do simply is grab that information and then fire it off to the client. Whoops. Having a little mouse difficulties here. Sorry about that. So all we need to do is, at this point, say, look, we fired it off into the Q thread or into the thread pool, which we haven't actually done yet. And when it's done, it'll pop back right here. So let's actually implement our thread. Now, what we want to do is we want to say Q thread pool. And then we want the global instance. Remember, we get, by default, an instance of the thread pool every time we make a Qt application. Because I believe Qt actually uses it underline hood too. So start, and we're going to say, my task. So we're just going to fire this off. And you could even do a little bit more voodoo magic here if you wanted to. Why is this complaining all of a sudden? Let me pause this and debug that real quick. Once again, sorry about that. I had an extra one of those brackets in there. Took me a minute to find that. But anyways, we make our global instance. And we're just going to start our task. And you could, if you really, really wanted to get kind of cool with this, is you could just say, at the very beginning of all of this, set max thread count. And we'll just say 15. So you can have 15 threads running at the same time if you really want them. Now, remember, when this whole thing fires off, it's going to do our work, meaning it's going to call my task and to jump down here. It's going to do our time-consuming project. So this is all happening on another thread, calling emit, which is why we do that queued connection, because that's going to be on a separate thread. So when the context of execution jumps back here, where it says right here, that's when we want this information to come back. If you try this any other way, you're going to get some really weird results. Either nothing's going to happen, or you're going to get something like queues socket cannot be queued off in a separate thread or some nonsense like that. So all we really want to do at this point is just grab that data back. So we're just going to say, let's do this, queue byte array, because we're going to send a buffer of information. We're just going to say buffer.append. We just want to know what the information was. So we're going to do this. And we're going to say queues string. We need to turn that number into a string, so we can throw that in here. And then we're just going to simply say socket write. And we want to write the buffer out. And actually, let's do socket write. And we just want to throw a, actually, we could just throw it right here. Sorry about that. Thinking faster than I was typing. We just want to throw character turn line feed in there. That way we can break this up a little bit. So let's run this, see if it works. Can that column number, blah, blah, blah, without an object, what did I do wrong here? Oh, duh. It's not setting on its number. Sorry about that. It's always kind of funny how, oh, I never, I was rushing, wasn't I? It's always kind of funny how it's in these massive, complex projects. It's the little things that really throw you up. And then you got to figure out what blew up and why. And that's one thing I do like about some of the managed languages out there like .NET and Java is it's pretty obvious what's wrong. You don't have to sit there and kind of go through it. All right, so let's do telnet. Open 127.001.1234. And when we hit a button, you see how it says task result 4950. That's the result of our task. And every time we hit a button, you can see it's doing that. Now that F is just me hitting the F on the keyboard. So what's happening here? Well, this server is fully asynchronous and fully threaded. So we're using two methods in one. And that's why this is an advanced topic. And I want to kind of go through this code just so we know exactly what's going on here. Let's start at the beginning. We create an instance of our server and call start server. The server code, where is it? There it is. It says, OK, listen on any address, port 1234. And then say, start it or not started. And then when we get incoming connections, make a new client and set the socket. Now, setSocket is something that we've actually made ourselves. Let's scroll up here. SetSocket, we're just making a new QTCP socket. And then we are wrapping the signals and slots. That way, we can tell what's going on with that underlying socket. And then, of course, we're calling setSocket descriptor. So we're getting the underlying socket mechanism. Now, all of these are asynchronous. And asynchronous means it does not run in the same context of your application. So all this information, reading and writing to the socket, that happens on an entirely different thread. It's asynchronous. And you don't have to worry about that thread. It's taken care of for you. But the main problem is, when it jumps up in here, this is your main thread of execution. So you have to do something. And that's going to become a time-consuming task. That time-consuming task could potentially deadlock your other applications. Now, if I'm dead wrong, somebody out there, please message me. But everything that I've read about QTSAZ, this is the asynchronous communications. And this is your context of execution. So this also is not asynchronous. I know that's kind of a double standard where it says not asynchronous. Basically, what it means is your code is running in your thread, not in QTSAZ. So if you want to make sure you're not deadlocking different sockets and different clients out there, you need to throw this back out onto a separate thread, aka the thread pool that we're using. And then do some work. And our time-consuming task, I mean, let's just even kick it out to 1,000, won't really make a bit of difference as far as the computer goes. But do your time-consuming task and then jump right back out here into task result. And then send the data out to the client. That way, your time-consuming task is done on a separate thread so that when other clients come in and you're already processing this, they're not going to sit there and wait. But you also protected yourself because you've done the max thread count 15. That way, if you have 3,000 people hit this thing all at the same time, your server doesn't just explode. I mean, it'll look like the Death Star just exploding across the night sky or something. So let's just compile this. And one more time, let's just see this thing in action. This was a fairly advanced tutorial. Like I said, this is one of the techniques that a lot of the pros use. Whoops, forgot to open. Silly me. Fortunately, a lot of the pros know to type open before they type the address. And you notice how we have a different number because we changed the result. So yeah, there you go. And all of its beauty. This is an advanced TCP socket server with asynchronous communications and multi-threaded. That is a mouthful. And believe me, it took me a while to get this thing actually running because I'm definitely not used to Qt the way they handle their sockets and their threading and signals and slots. And I'm still learning just like you are. I think when it comes to something like Qt, we're all going to be learning for a very long time. It's a very, very robust framework. I shouldn't call it a framework. It's a library. I've been kind of shunned for that before. But anyways, this is Brian. Thank you for watching. I hope you found this video educational and entertaining. Shoot me any questions or comments you got. I look forward to them. Just because I'm recording the video doesn't mean I'm instantly right. I've been wrong many, many times. And I've publicly admitted, hey, I'm wrong. And then I've given the person credit. So if you find a flaw in my program, let me know. Because I want to know about it. Because I'm going to be using the same code. That's it. Thanks.