The goal of this exercise is to write a server for the Sum protocol from ex. 1 of the previous TD.
Contrary to the case of ex. 1, we seek to write a sever that may
be have simultaneously OP_READ
and OP_WRITE
as operations of interest.
int
(4 bytes) in Big Endian. The server responds with an int
in Big Endian,
corresponding to the sum of the received integers. These exchanges continue until the client closes the connection for writing.
Starting from the draft ServerSumBetter.java, write a server the the Sum protocol.
For this server, the class Context
will contain
two ByteBuffer
: one for reading and one for writing. The method process
is used to transfer from the input ByteBuffer
to the output ByteBuffer
.
static private class Context { final private SelectionKey key; final private SocketChannel sc; final private ByteBuffer bbin = ByteBuffer.allocate(BUFFER_SIZE); final private ByteBuffer bbout = ByteBuffer.allocate(BUFFER_SIZE); private boolean closed = false; private Context(SelectionKey key){ this.key = key; this.sc = (SocketChannel) key.channel(); } private void process() { // TODO } private void updateInterestOps() { // TODO } private void doRead() throws IOException { // TODO } private void doWrite() throws IOException { // TODO } }
The jar ClientSumChrono.jar times the time it takes for a client to perform 100 000 sums.
Time your servers ServerSumBetter
and ServerSum
and compare their respective times.
% java fr.upem.net.tcp.blocking.ServerSumBetter 7777 % java -jar ClientSumChrono.jar localhost 7777
% java fr.upem.net.tcp.blocking.ServerSum 7777 % java -jar ClientSumChrono.jar localhost 7777
The goal of this exercise is to write a simple chat server. We will start from a chat where we only exchange INTs in BigEndian. Later on we will move on to a protocol where we do exchange messages.
The format of the messages that circulate between clients and the servers are simply INT en BigEndian
When a message is received by the server, it retransmits it to all of the connected clients.
Starting from the draft ServerChatInt.java, write a server for the ChatInt protocol in non-blocking mode.
The main difference with regard to the server we have seen so far is that,
when a message is received from a client, the server must retransmit it to all other connected clients.
In the draft code given, this will be the role of the method serverChatInt.broadcast
.
This method will iterate over the SelectionKey
corresponding to every client and call the method context.queueMessage
of the corresponding Context
object.
The method context.queueMessage
cannot simply write an INT into the output ByteBuffer
because it could be full. We will therefore use a
Queue<Integer>
to keep all of the arriving messages that are to be retransmitted to the corresponding client. It will be the method context.processOut
that will have the responsibility of filling the output ByteBuffer
by looking at the message queue.
In order to test your server, use the jar ClientChatInt.jar. This client sends the INT given in the command line and prints out the INT received from the server.
% java fr.upem.net.tcp.nonblocking.ServerChatInt 7777 % java -jar ClientChatInt.jar localhost 7777 % java -jar ClientChatInt.jar localhost 7777
When you write down a number on the keyboard in any one of the two clients, you should then see it printed on the screen in both.
A protocol message packet is made out of a login and a text in the following format:
+------------------+------------+------------------+------------+ | Login size (INT) | Login UTF8 | Text size (INT) | Texte UTF8 | +------------------+------------+------------------+------------+
The packets should not exceed 1024 bytes.
When a message packet is received by the server, it will broadcast it to all of the clients.
With regard to the previous protocol, the added difficulty comes from the fact that we need to discover
the message packets contained in ByteBuffer bbin
.
In order to do this, we propose an architecture based on the interface Reader.java. The interface Reader
is parametrized according to a type T
which is the target type to extract from the ByteBuffer
The methods of the interface must have the following behaviour:
ByteBuffer
. The convention will (always) that ByteBuffer bb
is in write-mode before and after the call to the method.enum Reader.ProcessStatus
.
REFILL
indicates that we have not yet seen enough data in order to return an object of type T
. Therefore we will need to feed more data into the Reader
.DONE
indicates that the Reader
is ready to produce an object of type T
, and the corresponding bytes have been read from ByteBuffer
. You can then retrieve the object produced by using the method get
.ERROR
indicate that the bytes read from ByteBuffer
do not comply with the expected formatT
corresponding to the read data (when the method process
has returned the status DONE
, else it throws an exception IllegalStateException
).Reader
object so that it may be used again. For example, we give you IntReader.java implementing the interface Reader
. This class extracts INT in BigEndian.
In ServerChatInt
, we may then rewrite the method processIn
as follows:
private void processIn() { for(;;){ Reader.ProcessStatus status = messageReader.process(bbIn); switch (status){ case DONE: Integer value = messageReader.get(); server.broadcast(value); messageReader.reset(); break; case REFILL: return; case ERROR: silentlyClose(); return; } } }
Write a class StringReader
implementing the interface Reader<String>
which extracts String
in the format:
+------------+------------+ | Size (INT) | Texte UTF8 | +------------+------------+The maximum size is 1024.
Verify your class with the unit tests StringReaderTest.java
Write a class MessageReader
implementing the class Reader<Message>
in order to read the message packets of the protocol Chaton.
Write a non-blocking server for the protocol Chaton.
In order to test your server, you may use the client ClientChat.jar.
% java -jar ClientChat.jar Bob localhost 7777