How to Create a Non-Blocking Server in Java?
Last Updated :
09 May, 2022
A Non-Blocking server means that it is able to have multiple requests in progress at the same time by the same process or thread because it uses Non-Blocking I/O. In the Non-Blocking approach - one thread can handle multiple queries at a time. A server using a Non-Blocking socket works in an asynchronous manner means once the request is received it lets the system perform the task on it, and continues to take other requests and once the task is over it responds according to each request. In this article, we see how we implement our own Non-blocking server and client. Let's first discuss Java NIO components and classes implementing Java NIO channels.
Selectors
The Selector is one of the Java NIO classes. The channels that we want to listen to must be registered in Selector. Each channel is assigned with selectionKey. SelectionKey is an object that identifies a channel and contains information about the channel like whether the channel is ready to accept the connection. It holds information about the type of the request and who is making the request. The instance of Selector can monitor more socket channels and it also informs the application to process the request. We can create a selector by calling the Selector.open() method:
Selector selector = Selector.open();
Buffers
Buffers define the core functionality which is common to all buffers the limit, capacity, and current position. Java NIO buffers are used for interacting with NIO channels. It is the block of memory into which we can write data, which we can later be read again. The memory block is wrapped with an NIO buffer object, which provides easier methods to work with the memory block. There is a buffer for each basic data type:
- CharBuffer
- DoubleBuffer
- IntBuffer
- ByteBuffer
- ShortBuffer
- FloatBuffer
- LongBuffer
Syntax to allocate the ByteBuffer with the capacity of 1024 bytes:
ByteBuffer buffer = ByteBuffer.allocate(1024);
Classes Implementing the Java NIO channel
SocketChannel
The Java NIO SocketChannel is used for connecting a channel with a TCP network socket. It is equivalent to Java Networking Sockets used in network programming. It also uses the factory methods for creating the new object. A SocketChannel in Java NIO can be created :
- When an incoming connection arrives at the ServerSocketChannel.
- To connect with a server anywhere using the internet.
We can open a SocketChannel by calling the SocketChannel.Open() method.
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 8089));
ServerSocketChannel
The Java NIO ServerSocketChannel is also used for connecting a channel with a TCP network socket. It is equivalent to Java Networking Sockets used in network programming. The class of ServerSocketChannel is located in the java.nio.channels package. A server-socket channel is created by invoking the .open() method of this class. A newly-created server-socket channel is open but not yet bound. An attempt to invoke the .accept() method of an unbound server-socket channel will cause a NotYetBoundException to be thrown. A server-socket channel can be bound by invoking one of the bind methods defined by this class.
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8089));
while(true) {
SocketChannel socketChannel = serverSocketChannel.accept();
}
Steps to Create a Non-Blocking Server
Project Structure (IntelliJ IDEA)
Step 1: Open Selector that handles channel
We create a Selector object by calling the static .open() method of the Selector class. A Selector is used to hold a reference to a set of channels and can be asked to supply a set of channels that are ready to accept the connection.
Syntax:
selector = Selector.open();
Step 2: Bind the Server Port
We create ServerSocketChannel by calling the .open() method, after that we call the .configureBlocking() method on it, passing in false as a parameter to tell the channel not to block. Then we obtain the ServerSocket from the channel by calling the .socket() method. We bind the socket to port 8089, so the server can start listening on that port.
ServerSocketChannel socket = ServerSocketChannel.open();
socket.configureBlocking(false);
ServerSocket serverSocket = socket.socket();
serverSocket.bind(new InetSocketAddress("localhost", 8089));
Step 3: Register ServerSocket to Selector
We need to register our server with the selector so that when operations are required to be performed, the server channel will be selected and the operations will be performed. This can be done by calling the .register() method. This registers the object of ServerSocketChannel to accept incoming connections.
socket.register(selector, ops, null);
Step 4: Wait for the event
Now we enter an infinite while loop and then call the .select() method of the selector, which selects a set of keys whose corresponding channels are ready for I/O operations. This method performs a blocking selection operation. It returns only after at least one channel is selected.
selector.select();
Step 5: Get the Selection Keys
We can get the selected keys that require an operation to be performed by calling the .selectedKeys() method of the selector, which returns the keys as a set.
Set<SelectionKey> selectedKeys = selector.selectedKeys();
We use an iterator by calling the .iterator() method that helps to traverse through all the keys in the set. We get the key by calling the .next() method of the iterator.
Iterator<SelectionKey> i = selectedKeys.iterator();
Step 6: Check whether the client is ready to accept
To test whether a channel corresponding to a key is ready to accept a new socket connection we use .isAcceptable() method. It behaves in exactly the same way as the expression key.readyOps() & OP_ACCEPT != 0
if (key.isAcceptable()) {
// New client has been accepted
}
Step 7: Read the Client's message
We register the channel for a read operation, as once the client's connection is accepted, the client will send the message back to the server.
client.register(selector, SelectionKey.OP_READ);
It will read data from the channel and put it into the buffer. After that, we will send data from the buffer onto the screen.
Step 8: Close the connection
Now closes the connection to the client by using the .close() method of the SocketChannel object.
Below is the implementation of the above-mentioned steps:
File: Server.java
Java
/*package whatever //do not write package name here */
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Server {
private static Selector selector = null;
public static void main(String[] args)
{
try {
selector = Selector.open();
// We have to set connection host,port and
// non-blocking mode
ServerSocketChannel serverSocketChannel
= ServerSocketChannel.open();
ServerSocket serverSocket
= serverSocketChannel.socket();
serverSocket.bind(
new InetSocketAddress("localhost", 8089));
serverSocketChannel.configureBlocking(false);
int ops = serverSocketChannel.validOps();
serverSocketChannel.register(selector, ops,
null);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys
= selector.selectedKeys();
Iterator<SelectionKey> i
= selectedKeys.iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
if (key.isAcceptable()) {
// New client has been accepted
handleAccept(serverSocketChannel,
key);
}
else if (key.isReadable()) {
// We can run non-blocking operation
// READ on our client
handleRead(key);
}
i.remove();
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
private static void
handleAccept(ServerSocketChannel mySocket,
SelectionKey key) throws IOException
{
System.out.println("Connection Accepted..");
// Accept the connection and set non-blocking mode
SocketChannel client = mySocket.accept();
client.configureBlocking(false);
// Register that client is reading this channel
client.register(selector, SelectionKey.OP_READ);
}
private static void handleRead(SelectionKey key)
throws IOException
{
System.out.println("Reading client's message.");
// create a ServerSocketChannel to read the request
SocketChannel client = (SocketChannel)key.channel();
// Create buffer to read data
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
// Parse data from buffer to String
String data = new String(buffer.array()).trim();
if (data.length() > 0) {
System.out.println("Received message: " + data);
if (data.equalsIgnoreCase(
"I Love KirikoChan")) {
client.close();
System.out.println("Connection closed...");
}
}
}
}
File: Client.java
Java
/*package whatever //do not write package name here */
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class Client {
public static void main(String[] args)
{
try {
String[] messages
= { "Non-Blocking servers are the best.",
"I Love GeeksForGeeks",
"I Love KirikoChan" };
System.out.println(
"Connection accepted by the Server..");
SocketChannel client = SocketChannel.open(
new InetSocketAddress("localhost", 8089));
for (String msg : messages) {
ByteBuffer buffer
= ByteBuffer.allocate(1024);
buffer.put(msg.getBytes());
buffer.flip();
int bytesWritten = client.write(buffer);
System.out.println(String.format(
"Sending Message: %s\nbufforBytes: %d",
msg, bytesWritten));
}
client.close();
System.out.println("Client connection closed");
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Connecting the client to our server
Now we have a client and a Non-Blocking server ready for communication. First, run the .main() inside the Server.java file it must be ready when the client sends a message.
Output: Server.java
The output of the Server.java fileOutput: Client.java
The output of the Server.java file
Similar Reads
Non Blocking Server in Java NIO
Java NIO(New Input/Output) is high-performance networking and file handling API and structure which works as an alternative IO API for Java. It is introduced from JDK 4 which works as the second I/O system after standard Java IO with some added advanced features. It provides enhanced support for fil
8 min read
How to Create a Socket at a Specific Port in Java?
A socket is one end-point of a two-way communication link between two programs running on the network. Socket classes are used to represent the connection between a client program and a server program. Socket Programming, us basically client-server programming where a socket is used as a link betwee
3 min read
Blocking Methods in Java
Blocking methods in java are the particular set of methods that block the thread until its operation is complete. So, they will have to block the current thread until the condition that fulfills their task is satisfied. Since, in nature, these methods are blocking so-called blocking methods. For exa
4 min read
How to create a multi-chat server using UDP?
In this article, we will see the development of a Multi-Threaded UDP Chat Server-Client Network for Data Transfer in Linux. UDP is used for low latency and connectionless characteristics, the architecture consists of a server managing multiple clients through threading. This networked chat applicati
10 min read
How to create telnet client with asyncio in Python
Telnet is a client/server application protocol that uses TCP/IP for connection. Telnet protocol enables a user to log onto and use a remote computer as though they were connected directly to it within the local network. The system that is being used by the user for the connection is the client and t
4 min read
BlockingQueue add() in Java with examples
The add(E e) method of BlockingQueue interface inserts the element passed in the parameter to the end of the Queue is there is space. If the BlockingQueue os capacity restricted and no space is left for insertion, it returns an IllegalStateException. Syntax:Â public void add(E e)Parameters: This met
2 min read
BlockingQueue offer() method in Java with examples
There are two types of offer() method for BlockingQueue interface:Note: The offer() method of BlockingQueue has been inherited from the Queue class in Java. offer(E e, long timeout, TimeUnit unit) The offer(E e, long timeout, TimeUnit unit) method of BlockingQueue inserts the element passed as param
6 min read
BlockingDeque push() method in Java with examples
The push(E e) method of BlockingDeque pushes an element onto the stack represented by this deque. It inserts the element passed in the parameter to the front of the Deque if there is space. If the BlockingDeque is capacity restricted and no space is left for insertion, it returns an IllegalStateExce
2 min read
BlockingQueue take() method in Java with examples
The take() method of BlockingQueue interface is used to retrieve and remove the head of this queue. If the queue is empty then it will wait until an element becomes available. This method is more efficient if working on threads and using BlockingQueue in that process. So the thread that initially ca
4 min read
Java Thread Safety and How to Achieve it?
As we know Java has a feature, Multithreading, which is a process of running multiple threads simultaneously. When multiple threads are working on the same data, and the value of our data is changing, that scenario is not thread-safe and we will get inconsistent results. When a thread is already wor
5 min read