In this series of exercices, you are going to write more and more sophisticated blocking TCP servers for the LongSum
protocol.
long
integers using the following TCP protocol:
IterativeLongSumServer
which treat its clients one after the other.OnDemandLongSumServer
which starts a new thread to treat each new client.BoundedOnDemandLongSumServer
server which starts a new thread for each client but which limits the number of clients which are simultaneously connected.FixedPreStartedLongSumServer
which pre-starts a fixed number of threads which each are going to treat client in a loop. As opposed to the previous exerices, these threads are reused to treat multiple clients one after the other.FixedPreStartedLongSumServer
with an interactive console and the with the ability to automatically disconnect inactive clients.
The first verion of the server, IterativeLongSumServer
,
simply accepts a client, treat its requests (i.e., there might be
several sums) and when the client closes the connexion, the server
accepts a new client and so on ...
To write this server, you will use the template
IterativeLongSumServer.java.
The full treatment of the connexion with a client is performed by the method serve(SocketChannel client)
. Exceptions raised during the method serve
are thrown and are handled in the launch
method.
Write the code for the serve
method.
You can test your server using the client ClientLongSum
you wrote during the previous session.
% java fr.upem.net.tcp.IterativeLongSumServer 7777 % java fr.upem.net.tcp.ClientLongSum localhost 7777
You must also test using the jar file ClientLongSumVerbose.jar, which connects to the server and sends 5 sums waiting in between sums a delay taken as a parameter on the command line.
% java -jar ClientLongSumVerbose.jar 7777 % java fr.upem.net.tcp.ClientLongSum localhost 7777 100
Until now, you only tested your server with one client. Open 4 terminals. In one of them, launch your server and the 3 others launch
de>ClientLongSumVerbose.jar with a large delay (for instance 5000) as in the image below:
Explain how 3 clients can connect simultaneously to your server despite the fact that your server only treats one client at the time.
We now want to allow several clients to simultaneously connect to the server. For this, we are going to create a new thread to treat each new client.
A first method, quite easy to implement, consists in simply creating a new thread each time a new client is accepted and use this thread to treat the client.
Using the same template as for IterativeLongSumServer
, write a new class
OnDemandConcurrentLongSumServer
.
You can monitor the number of threads used by your
server using jconsole
. You should see that the number
of threads increases with each new connected client.
The problem with this approach is that the number of threads started by the server is unbounded. This means that the server can crash under the load if too many clients connect at the same time.
We now want to write a class BoundedOnDemandConcurrentLongSumServer
which ensures that the number of threads concurrently started is at most maxClient
(an argument taken from the command line).
Semaphore
. The
class Semaphore
is thread-safe. An object of the class Semaphore
is created with a certain amount of permits.
Semaphore semaphore = new Semaphore(nbPermits);The
semaphore.acquire()
method tries to take a permits from the semaphore if there is one and otherwise it blocks until a permits becomes available. The semaphore.release()
method put a permit back in the semaphore.
We write the class BoundedOnDemandConcurrentLongSumServer
.
The main problem with the solution proposed in the previous exercise is that each time a client is accepted a new thread needs to be started before the client can be treated.
The idea we are going to explore in this exercise is to start
(when the server is launched) a fixed number of threads which will each act as an iteratif serveur (cf. Exercice 1). The mutual exclusion between these worker threads is ensured by the call to
serverSocketChannel.accept()
(which is thread-safe).
Write a server
FixedPrestartedLongSumServer
which implements this idea. The server will take on the command line the number maxClients
of worker threads.
What happens to the clients who attempt to connect to your server when maxClient
are already connected ?
In this exercise, we are going to see how to handle inactive clients in a FixedPreStarted
server. Indeed these clients are taking ressources from the server.
The problem with detecting inactive clients is that the method
socketChannel.read
and socketChannel.write
are blocking. Hence if a worker thread calls socketChannel.read
and the client does not write anything, the worker thread is blocked for ever.
Hence we will need to start a new thread (one for the server) which will have the task of disconnecting inactive clients.
There are two main problems to solve:
To solve these two problems, you can implement a class ThreadData
which is threadsafe and which implements following two methods:
setSocketChannel(SocketChannel client)
which sets the
socketChannel
;tick()
which indicates that the client is active;closeIfInactive(int timeout)
which disconnect the client
if it has not been active in the past timeout milliseconds;close()
which disconnect the client.There will one ThreadData
object for each worker thread. A unique thread while call the method closeIfInactive(int timeout)
on each of the threadData
objects every timeout milliseconds.
The easiest way to code the class ThreadData
is to store the SocketChannel
of the current client and a long
corresponding to the time (obtained with System.currentTimeMillis()
) of the last activity.
Starting with the class FixedPrestartedConcurrentLongSumServer
, write a class FixedPrestartedConcurrentLongSumServerWithTimeout
which properly handles inactive clients.
To test your code, you can use:
% java -jar ClientLongSumVerbose.jar localhost 7777 10000 % java -jar ClientLongSumVerbose.jar localhost 7777 1000 % java fr/upem/net/tcp/FixedPreStartedLongSumServerWithTimeout 7777If the timeout of your server is of 2000 milliseconds, the first
ClientLongSumVerbose
should be disconnected by the server
(and hence raise en IOException) but the second ClientLongSumVerbose
should not be affected.
On souhaite maintenant rajouter une console au serveur: c'est-à-dire la possibilité de lire des instructions au clavier.
Votre console devra reconnaître trois instructions:
Rajouter une console à votre serveur
FixedPrestartedConcurrentLongSumServerWithTimeout
de l'exercice précédent.