220 likes | 360 Views
Practical Session 11. Multi Client-Server Java NIO. The Server Interface. interface ServerProtocol : String processMessage (String msg ); boolean isEnd (String msg ); In order to organize the work of the Server, and to allow several protocols, we create an interface:
E N D
Practical Session 11 Multi Client-Server Java NIO
The Server Interface • interface ServerProtocol: • String processMessage(String msg); • booleanisEnd(String msg); • In order to organize the work of the Server, and to allow several protocols, we create an interface: • processMessage: a function that decides what to do with the content received. • isEnd: returns true if the message equals our ‘exit’ command.
Supporting Multiple Clients • The basic flow of logic in such a server is this: while (true) { accept a connection; create a thread to deal with the client; } • The thread reads from and writes to the client connection as necessary.
ConnectionHandler • In order to allow more than one client to connect to our server, we need to run each connection in its own thread. • ConnectionHandler is a Runnable that handles one connection. • Each client that wishes to connect to our server initiates the connection. • The server accepts the connection from the client. • The socket returned is sent to the ConnectionHandler object. • ConnectionHandler is run as a Thread. • Server goes back to blocking mode, waiting for a new connection.
MultiClient – Server Implementation • ServerProtocol [Interface] • processMessage • isEnd • EchoProtocol implements ServerProtocol • processMessage: message received is returned • isEnd: returns true if message is ‘bye’ • ConnectionHandler[Runnable] • Receives messages from client [msg = in.readLine()] • Processes message [using process()] using EchoProtocol • If message is ‘bye’, exits thread [protocol.isEnd(msg)] • Else, message is sent back to client [out.println(response)] • Sends returned result from processing to client. • MultipleClientProtocolServer [Runnable] • Our server – runs as a thread • Creates ServerSocket, listens to a port • Runs ConnectionHandler in a thread once accept() returns a socket. http://www.cs.bgu.ac.il/~spl141/PracticalSession10/MultipleClientProtocolServer
Java Non-blocking IO • In our examples, the server gets stuck on • msg = in.readLine() • clientSocket = serverSocket.accept() • out.println(msg) • You cannot do something else while these methods are blocking. (process client messages, handle other clients etc.).
Java Non-blocking IO • Solution? java.nio. • The package is an efficient InputOutput package, which supports Non-blocking IO. • NIO Concepts: • Channels • Buffers • Selectors • Tutorial: http://tutorials.jenkov.com/java-nio/index.html
Channels [our sockets] • An Object you can read from and write to. • Channels can be either blocking (by default) or non-blocking. • SocketChannel: • http://docs.oracle.com/javase/1.4.2/docs/api/java/nio/channels/SocketChannel.html • Same as regular Socket object. • Difference: read(), write() can be non-blocking. • ServerSocketChannel: • http://docs.oracle.com/javase/1.4.2/docs/api/java/nio/channels/ServerSocketChannel.html • accept() returns SocketChannel • Same as regular ServerSocket object. • Difference: accept() can be non-blocking. • Does not block until a client connects. • Checks if a client is trying to connect, if so returns a new socket, otherwise returns null!
Setting up ServerSocketChannel and SocketChannel • ServerSocketChannel : • int port = 9999; • ServerSocketChannelssChannel = ServerSocketChannel.open(); • ssChannel.configureBlocking(false); • ssChannel.socket().bind(new InetSocketAddress(port)); • SocketChannel: • SocketChannelsChannel=SocketChannel.open(); • sChannel.connect(new InetSocketAddress("host/ip", 9999)); • sChannel.configureBlocking(false); http://docs.oracle.com/javase/1.4.2/docs/api/java/net/InetSocketAddress.html
Buffers [our containers] • The objects which hold the data to be sent and data received. • Channels know how to read and write into Buffers, and buffers can read and write into other buffers. • Java NIO comes with the following Buffer types: • ByteBuffer • CharBuffer • DoubleBuffer • FloatBuffer • IntBuffer • LongBuffer • ShortBuffer http://docs.oracle.com/javase/1.4.2/docs/api/java/nio/ByteBuffer.html
Buffer IO Operations • Readingfrom a channel to a buffer: numBytesRead = socketChannel.read(buf); • Contents found in socketChannel are read from their internal container object to our buffer. • Writingfrom a buffer to a channel: numBytesWritten = socketChannel.write(buf); • Contents from our buf object are written to the socketChannel’s internal container to be sent. • If read or write returns -1, it means that the channel is closed. • Read and write operations on Buffers update the position marker accordingly.
Creating Buffers • We'll be using ByteBuffer. • These are buffers that hold bytes. • Creating a new ByteBuffer: [Method I] • final int NUM_OF_BYTES = 1024; • ByteBuffer buffer = ByteBuffer.allocate(NUM_OF_BYTES); • Creating a new ByteBuffer: [Method II] • String message = “Sentence to write into buffer”; • ByteBuffer buffer = ByteBuffer.wrap(message.getBytes(),”UTF-8”);
Buffer Markers • Each buffer has capacity, limit, and position markers. • Capacity: • Being a memory block, a Buffer has a certain fixed size, also called its "capacity". • You can only write capacity bytes, longs, chars etc. into the Buffer. • Once the Buffer is full, you need to empty it (read the data, or clear it) before you can write more data into it. • Position: • Writing data to buffer: • Initially the position is 0. • When a byte, long etc. has been written into the Buffer the position is advanced to point to the next cell in the buffer to insert data into. • Position can maximally become capacity - 1. • Reading data from buffer: • When you flip a Buffer from writing mode to reading mode, the position is reset to 0. • As you read data from the Buffer you do so from position, and position is advanced to next position to read. • Limit: • In write mode: • The limit of a Buffer is the limit of how much data you can write into the buffer. • The limit is equal to the capacity of the Buffer. • When flipping the Buffer into read mode: • The limit means the limit of how much data you can read from the data. • When flipping a Buffer into read mode, limit is set to write position of the write mode. • In other words, you can read as many bytes as were written (limit is set to the number of bytes written, which is marked by position).
read/write operations • A read operation readsa specified number of bytes from the current position, and updates the position marker to point to the yet unread bytes. • A write operation writes some bytes from the current position, and then advances the position depending on the number of written bytes. • You can't read or write more than the limit of the buffer. • You can't increase the limit over the capacity. • It can be described as:
Buffer Flipping • The flip() method switches a Buffer from writing mode to reading mode. • Calling flip() sets the position back to 0, and sets the limit to where position just was. • The position marker now marks the reading position, and limit marks how many bytes were written into the buffer - the limit of how many bytes, chars etc. that can be read. • Example: • You create a ByteBuffer. • Write data into the buffer. • Flip() • Send the buffer to the channel.
More ByteBuffer Methods • clear(): • Makes a buffer ready for a new sequence of channel-read or relative put operations. • Sets the limit to the capacity . • Sets the position to zero. • rewind(): • Makes a buffer ready for re-reading the data that it already contains. • Leaves the limit unchanged. • Sets the position to zero.
StringMessageTokenizerInterface • void addBytes(ByteBuffer bytes); • Receives a Buffer of bytes containing data to be converted to chars. • booleanhasMessage(); • Is there a complete message ready? • String nextMessage(); • Get the next complete message if it exists, advancing the tokenizer to the next message.
FixedSeparatorMessageTokenizer • FixedSeparatorMessageTokenizer(String separator, Charset charset) • The constructor. • Between two messages in the buffer we have a separator. • Messages are encoded [chars to bytes] and decoded [bytes to chars] using the given charset [ASCII, UTF-8] • public synchronized void addBytes(ByteBuffer bytes): • Array of bytes received is converted to string and concatenated to the ones before it. • public synchronized booleanhasMessage(): • Checks if the buffer has a complete message, if so true, otherwise false. [done by checking if the separator exists in the string array] • public synchronized String nextMessage() • Returns the next complete message in the buffer.
NIO Echo Client • http://www.cs.bgu.ac.il/~spl141/PracticalSession10/NIOEchoClient
C++ Echo Client • Connect a socket to a host and port: tcp::socket socket; tcp::endpoint endpoint(boost::asio::ip::address::from_string(hostIPValue), portValue); boost::system::error_code error; socket.connect(endpoint, error); • Read from socket: buffer(startingPointPointer, sizeToReadInBytes, exception) //exception is optional //tmp holds the number of bytes read so far; bytes: an array of chars to read the received bytes from socket.read_some(boost::asio::buffer(bytes+tmp, bytesToRead-tmp), error) • Write to socket: socket.write_some(boost::asio::buffer(bytes + tmp, bytesToWrite - tmp), error) • Example: • 05_Boost_Echo_Client