- [Tim] All right, so at this point
you've written your first client and server,
but the server that we've written so far isn't very useful,
and that's because it can only connect to one client.
So let's modify it so that it can now accept
connexions from multiple clients.
So to do that, what we need to do is,
firstly I'll maximise the server edition.
So for the server what we need to do is change the call
to accept how it takes place within the loop,
and that's because if it's only called once,
it can only ever accept one connexion,
but if it's inside the loop, the server will accept
a connexion, create a new socket for the client,
create the input and output streams for that client socket,
and then loop around and call accept again,
and that's what we want to do so
that we can support multiple clients.
So to do that, we need to move things around a little bit,
so I'm going to grab this code here, the socket,
socket line right down to and including
the print buffer, the print output line.
K, you're gonna cut that and then paste
it into the while loop.
So basically all I've done at this point
is move everything inside the while(true) loop,
again, so that the server could loop back
and accept more client connexions.
So let's now try this out, so we're going
to exit our fullscreen mode, and I'm gonna run the server,
and that's still working like it did before,
and this time I'm gonna come over here to
the client, let's start a copy of that.
Client Connected as you can see,
we can put a test in there, Test, that's working correctly.
Let's now run another client while
the first one's still running.
So I come over here and click on run again,
and you see I've got Client Connected showing on
the left-hand side, and we've got two tabs here now,
so the two copies of the client running now.
The word Demo there just to make sure it's working,
and you can see that's working as well now,
so we've got two clients connecting to our server.
All right, great, so at this point we've
got multiple clients that can connect to the server,
but the server code still has got, has problems.
Now can you see what those problems are?
Just take a minute to think about
what problems there could exist in the server code.
All right, so let's now demonstrate the problem.
Well actually, what I'll do first is I'll go back
and exit from both clients, so let's do that first.
Now depending on the order that I do this,
the server may not shut down, so I'm gonna click on exit,
or type exit rather, and then back here
and do the second one like so,
and you can see that I've actually closed down
both clients here, both exited,
but the server hasn't closed down.
Now in a real-world situation,
that wouldn't be a problem because usually you want
the server to be running 24/7, but if you ever wanted
to bring down, or terminate the server,
you'd then do so manually in that scenario.
So we don't worry about this particular problem,
if necessary we can terminate the server manually.
So to terminate manually we can just close
it down, Terminate, and close it down
as we would any programme that was in a loop.
All right, so let's try this again,
I'm gonna run the server again this time.
Okay, I'm gonna come back over,
and I'm going to run this client again.
This time I'm not gonna type anything into the console,
now I'm gonna go back and run it again,
run a second copy of it.
Now we should have seen two Client Connected messages there
but there's only one, now because we didn't type anything in
the first client, the server code is actually blocked,
waiting for input, it's actually blocked.
If we come back and have a look over here,
at the input.readLine(), line 21 there.
So basically the first iteration
of the loop hasn't completed, and so
the server hasn't called accept again.
So we go back now and, back to our client,
and actually type something for
the first one, and we can then see that
the second client connect, two Client Connected messages
are now showing in the left-hand window.
Also keep in mind that each client
will only ever see one message echoed back to it.
To see that in action, if we actually close down,
well just to confirm before I do that, by the way,
that the other one was still working.
That's now working, but if we close down both of these...
And then we start it up again, we got Client Connected
as you can see there, and you can see
that when we type the second one this time,
we actually didn't get that response back.
So in other words, the second message
wasn't echoed back, and that's because
the server's now blocked all the accept calls.
So if you run the second client...
We've got the same scenario there,
that it only accepts, or echoes rather,
back the first message, and not the second one,
'cause the server's now blocked on the accept call again.
All right, so let's terminate everything here now,
and terminate the server as well.
What we're gonna do now is make a change to
the server code to simulate a long-running task.
Now because you need time to switch to
a second client and type something into its console,
we're gonna have the server sleep for 30 seconds
every time it receives input from the client,
and we can change the sleep time
if that's too long, or too short.
I'll show you how to do that.
So I'm gonna update the server as following,
so we're gonna come up here after
the string, echo equals input.readLine(),
and we're gonna put a try/catch there.
try, we're gonna go Thread.sleep,
I'm gonna make this 15 seconds, 15000,
but you can make it any length of time you'd like.
catch(InterruptedException e), and we'll print out
if we do get that exception, "Thread interrupted."
All right, so what we're gonna do now
is run the server, then run the first client,
but then we're not going to type anything in
the console for the first client,
then we're gonna run the second client
and find that the second client
won't be able to respond, or connect rather,
until the server reads data from the first client.
So we're gonna go back to the first client and type in
a string, and then switch back and see what happens.
So let's see how all this works.
So I'm gonna run the server, okay,
I'm going to run a copy of the first.
K, Client Connected, but I'm now going
to click on run again to run a second copy of it,
but then I'm gonna switch back to the first one
because at the moment we need to go back to the first one.
Type something in there, nothing's working
for there at the moment.
Let's switch back to the second client,
Client's Connected as you can see there now,
but we haven't got anything echoing.
Go back to the first one, we've now got an echo.
Second one, finally we got the echo back,
and that's got to do with this delay, this Thread.sleep.
So there's basically a delay there
before we get the echo back.
So what's actually the cause of all these problems?
Well if you guessed that it's because
the server's doing everything on a single thread,
I'm gonna give you a gold star.
So I mentioned in the previous video
that when writing networking code,
you need to be familiar with threads,
and this is actually the reason why.
Servers that can connect to multiple clients
will run multiple threads.
So what we're gonna do now is we're going to update
the code so that the server handles
each client on a separate thread,
and this is gonna do two things.
Firstly, it's gonna let the server be more responsive
to client connexion requests,
and secondly it's gonna prevent one client
from hogging the server for too long a period.
So I'm gonna close down the client and the servers,
or client and server I should say.
Okay, and we'll have a look at our server code again.
All right, so on the server's Main thread
we're going to call the accept method,
but whenever it accepts a connexion,
what we're gonna do this time is kick off a new thread for
the client, and then call the accept method again.
So each client thread will be responsible
for creating the input/output streams,
listening for requests on the client,
and then responding to those events.
So what we're going to do is create a new class,
new Java class, we'll call this one Echoer,
and the class is going to extend Thread,
that's java.lang.Thread, and we're gonna need
a private Socket socket, and we want a constructor,
and the constructor is going to just initialise the sockets,
so it's public Echoer accepting a socket,
and this.socket is equal to socket.
So now what we're gonna do is add the run method,
and that's gonna be called when the thread is started.
Now the code here is gonna be very familiar since it's
the same as what we're currently doing in the main method.
What is gonna be a bit different is
that we're not gonna use a try with resources statement,
and that's because the method doesn't create the socket
when the server connects to a client.
So you have to explicitly close
the socket when we're finished with it.
So let's go ahead and type this code in.
So I'll @Override run, and we're gonna change the super.run.
We'll put a try in here, then we're gonna catch
an IOException, and if we do get one,
then we're just gonna print out a message,
"Oops: " plus e.getMessage(),
and we also want a finally in here as well,
and finally needs to have a try,
'cause we're not using try with resources,
it's gonna be socket.close,
then we wanna catch an IOException on that as well,
at the moment we won't flag that in any way,
but we could log that if we wanted to.
All right, so in terms of the code,
BufferedReader input is equal to new BufferedReader,
new InputStreamReader(socket.getInputStream()),
same as we did previously.
All right, then we wanna PrintWriter writer
is equal to new PrintWriter(socket.getOutputStream(),
passing true as the second argument for flushing reasons,
then we're gonna add a while loop,
while(true) String echoString is equal to input.readLine().
if(echoString. is equal to, or equals "exit",
we're gonna break out of the while loop,
'cause of course it's an infinite loop,
otherwise we're still in the loop,
output.println, instead of writer we'll call that output,
and let's echo the string.
All right, so that's our Echoer class,
so what we wanna now do is go back
to our main method from our Main class,
we're gonna change the code to this now.
We're gonna leave our try with resources in there,
but we're getting rid of this code in here.
I perhaps could have copied all that instead
of typing it out again, but in any event,
we're getting rid of all that code.
That's now going to be while(true) still, but let's go
to the new Echoer(serverSocket.accept().start()).
There should have been an extra parentheses there, like so.
We're still catching an IOException,
and we'll show that server exception
if we actually get one there now.
So basically now we're creating
the server socket as before,
and still writing an infinite loop.
We're creating a new server socket,
while(true) infinite loop,
but this time we're kicking off a new thread
every time we accept a connexion,
so new Echoer(serverSocket.accept()),
then .start(), we're kicking off the Echoer run method,
and recall that the accept method returns
a socket for the client, and that's what
the serverSocket.accept() does,
and we're returning that, or sending that if you like,
to the Echoer in the constructor,
and then calling the start() method.
So I guess we could've changed the code there,
we could've written it instead, like this,
a Socket socket is equal to serverSocket.accept(),
Echoer echoer = new Echoer(socket),
echoer.start(), so we could've,
that's basically the equivalent code there.
I'll comment that out so you've got a copy of that
if you need to download the source code,
but the line that I've got here
on line 14 is basically doing exactly
what those three lines of code are doing.
I'll paste that at the top there.
All right, so let's finish the video here
because it's getting quite long.
In the next video we'll start testing
this multi-threaded server that we've written,
so I'll see you in the next video.