DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Data
  4. Java IO and NIO

Java IO and NIO

Check out this post to learn more about Java's blocking and non-blocking alternative to creating sockets.

Sergio Martin user avatar by
Sergio Martin
·
Jul. 05, 19 · Presentation
Like (6)
Save
Tweet
Share
18.71K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

Sockets use TCP/IP transport protocol and are the last piece of network communication between two hosts. You do not usually have to deal with them since there are protocols built on top of them like HTTP or FTP; however, it is important to know how they work.

TCP: It is a reliable data transfer protocol that ensures that the data sent is complete and correct and requires to establish a connection.

Java offers a blocking and non-blocking alternative to create sockets, and depending on your requirements, you might consider the one or the other.

Java Blocking IO

The Java blocking IO API is included in JDK under the package java.net and is generally the simplest to use.

This API is based on flows of byte streams and character streams that can be read or written. There is not an index that you can use to move forth and back, like in an array, it is simply a continuous flow of data.

Spring Bean Creation Lifecycle

Spring Bean Creation Lifecycle

Every time a client requests a connection to the server, it will block a thread. Therefore, we have to create a pool of threads large enough if we expect to have many simultaneous connections.

ServerSocket serverSocket = new ServerSocket(PORT_NUMBER);

while (true) {
    Socket client = serverSocket.accept();
    try {
        BufferedReader in = new BufferedReader(
            new InputStreamReader(client.getInputStream())
        );
        OutputStream out = client.getOutputStream();
        in.lines().forEach(line -> {
            try {
                out.write(line.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        client.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}


  1. A ServerSocket is created with a given port to listen on.
  2. The server will block when accept() is invoked and starts listening for clients connections.
  3. If a client requests a connection, a Socket is returned by accept().
  4. Now, we can read from the client (InputStream) and send data back to the client (OutputStream).

If we want to allow multiple connections, we have to create a Thread Pool:

ExecutorService threadPool = Executors.newFixedThreadPool(100);

 threadPool.execute(() -> {
     // SOCKET CREATION
 });


As you can see, this API has some limitations. We won’t be able to accept more connections than threads available in our machine. Therefore, if you are expecting to have many connections, you need an alternative.

Java NIO

Java.nio is a non-blocking API for socket connections, which means you are not tight to the number of threads available. With this library, one thread can handle multiple connections at once.

Spring Bean Creation Lifecycle
Main elements:
  • Channel: channels are a combination of input and output streams, so they allow you to read and write, and they use buffers to do these operations.
  • Buffer: it is a block of memory used to read from a Channel and write into it. When you want to read data from a Buffer, you need to invokeflip(), so that it will set pos to 0.
    int read = socketChannel.read(buffer); // pos = n & lim = 1024
    while (read != -1) {
      buffer.flip(); // set buffer in read mode - pos = 0 & lim = n
      while(buffer.hasRemaining()){
          System.out.print((char) buffer.get()); // read 1 byte at a time
      }
      buffer.clear(); // make buffer ready for writing - pos = 0 & lim = 1024
      read = socketChannel.read(buffer); // set to -1
    }
    1. On line 1, pos will be equals to the number of bytes written into the Buffer.
    2. On line 3, flip() is called to set position to 0 and limit to the number of bytes previously written.
    3. On line 5, it reads from Buffer one byte at a time up to the limit.
    4. On line 7, finally, we clear the Buffer.
  • Selector: A Selector can register multiple Channels and will check which ones are ready for accepting new connections. Similar to accept() method of blocking IO, when select() is invoked, it will block the application until a Channel is ready to do an operation. Because a Selector can register many channels, only one thread is required to handle multiple connections.
  • Selection Key: It contains properties for a particular Channel (interest set, ready set, selector/channel, and an optional attached object). Selection keys are mainly used to know the current interest of the channel (isAcceptable(), isReadable(), isWritable()), get the channel and do operations with that channel.

Example

We will use an Echo Socket Channel server to show how NIO works.

var serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(8080));

var selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select();
    var keys = selector.selectedKeys().iterator();

    while (keys.hasNext()) {
        var selectionKey = (SelectionKey) keys.next();

        if (selectionKey.isAcceptable()) {
            createChannel(serverSocketChannel, selectionKey);
        } else if (selectionKey.isReadable()) {
            doRead(selectionKey);
        } else if (selectionKey.isWritable()) {
            doWrite(selectionKey);
        }
        keys.remove();
    }
}


  1. From lines 1 to 3, a ServerSocketChannel is created, and you have to set it to non-blocking mode explicitly. The socket is also configured to listen on port 8080.
  2. On lines 5 and 6, a Selector is created and ServerSocketChannel is registered on the Selector with a SelectionKey pointing to ACCEPT operations.
  3. To keep the application listening all the time, the blocking method select() is inside an infinite while loop and select() will return when at least one channel is selected wakeup() is invoked or the thread is interrupted.
  4. Then, on line 10, a set of keys are returned from the Selector and we will iterate through them in order to execute the ready channels.
private static void createChannel(ServerSocketChannel serverSocketChannel, SelectionKey selectionKey) throws IOException {
    var socketChannel = serverSocketChannel.accept();
    LOGGER.info("Accepted connection from " + socketChannel);
    socketChannel.configureBlocking(false);
    socketChannel.write(ByteBuffer.wrap(("Welcome: " + socketChannel.getRemoteAddress() +
            "\nThe thread assigned to you is: " + Thread.currentThread().getId() + "\n").getBytes()));
    dataMap.put(socketChannel, new LinkedList<>()); // store socket connection
    LOGGER.info("Total clients connected: " + dataMap.size());
    socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ); // selector pointing to READ operation
}


  1. Every time a new connection is created, isAcceptable() will be true and a new Channel will be registered into the Selector.
  2. To keep track of the data of each channel, it is put in a Map with the socket channel as the key and a list of ByteBuffers.
  3. Then, the selector will point to a READ operation.
private static void doRead(SelectionKey selectionKey) throws IOException {
    LOGGER.info("Reading...");
    var socketChannel = (SocketChannel) selectionKey.channel();
    var byteBuffer = ByteBuffer.allocate(1024); // pos=0 & lim=1024
    int read = socketChannel.read(byteBuffer); // pos=numberOfBytes & lim=1024
    if (read == -1) { // if connection is closed by the client
        doClose(socketChannel);
    } else {
        byteBuffer.flip(); // put buffer in read mode by setting pos=0 and lim=numberOfBytes
        dataMap.get(socketChannel).add(byteBuffer); // find socket channel and add new byteBuffer queue
        selectionKey.interestOps(SelectionKey.OP_WRITE); // set mode to WRITE to send data
    }
}


  1. In the read block, the channel will be retrieved and the incoming data will be written into a ByteBuffer.
  2. On line 6, we check if the connection has been closed.
  3. On lines 9 and 10, the buffer is set to read mode with flip() and added to the Map.
  4. Then, interestOps() is invoked to point to WRITE operation.
private static void doWrite(SelectionKey selectionKey) throws IOException {
    LOGGER.info("Writing...");
    var socketChannel = (SocketChannel) selectionKey.channel();
    var pendingData = dataMap.get(socketChannel); // find channel
    while (!pendingData.isEmpty()) { // start sending to client from queue
        var buf = pendingData.poll();
        socketChannel.write(buf);
    }
    selectionKey.interestOps(SelectionKey.OP_READ); // change the key to READ
}


  1. Once again, the channel is retrieved in order to write the data saved in the Map into it.
  2. Then, we set the Selector to READ operations.
private static void doClose(SocketChannel socketChannel) throws IOException {
    dataMap.remove(socketChannel);
    var socket = socketChannel.socket();
    var remoteSocketAddress = socket.getRemoteSocketAddress();
    LOGGER.info("Connection closed by client: " + remoteSocketAddress);
    socketChannel.close(); // closes channel and cancels selection key
}


  1. In case the connection is closed, the channel is removed from the Map and we close the channel.

Java IO Vs. NIO

Choosing between IO and NIO will depend on the use case. For fewer connections and a simple solution, IO might be a better fit for you. Whereas, if you want something more efficient that can handle thousands of connections simultaneously, NIO is probably a better choice, but bear in mind that it will introduce much code complexity. However, there are frameworks like Netty or Apache MINA that are built on top of NIO and hide the programming complexity.

Java (programming language) Connection (dance) Data (computing)

Published at DZone with permission of Sergio Martin. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • When Should We Move to Microservices?
  • Steel Threads Are a Technique That Will Make You a Better Engineer
  • Integrate AWS Secrets Manager in Spring Boot Application
  • Demystifying the Infrastructure as Code Landscape

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: