Java Client Server Application
|

Multi-Threading in Java – Client Server Application Example

Multithreading in java?

Multithreading in java is a process of executing multiple threads simultaneously. A thread is a lightweight sub-process, the smallest unit of processing. Multiprocessing and multithreading, both are used to achieve multitasking. … Java Multithreading is mostly used in games, animation, etc

Every modern operating system has the support for multi-threading–so does the application built to run on that operating system. No, doubt multi-threading programming increase the performance and concurrency in our application. For certain applications, it is easier to code different procedures calls and conversation in a different thread than to manage them from the same thread.

Mostly in a client-server application, you definitely want the create the server application in a multi-threaded environment where thousands of clients connected to one server. Well if you think that we’re going to build a client-server application than you’re right. Wait! don’t just close the tab because this time I have got a real-world problem.

Multithreading Meme

Application Overview

First, a little intro to my application so that we’re all on the same page. The application is about to build a smart meter (GPRS Meter) data collector. The application simply opens up a ServerSocket and after that, it accepts clients (Smart Meter) waiting for the connection. There are thousands of smart meters trying to connect to that application. So, when the smart meter connects with the application via socket, the application creates its own separate thread for communication.

Promgramming thread meme

Get Started

The application that we’re going to build will be in Java programming language. So, the first step was simple, I try to open up the ServerSocket holding the Pepsi can in my hand.

public class DeviceListenerRunnable implements Runnable {

    private final int portNumber;  // 1
    private final ServerSocket serverSocket;  
    private final IConnectionEventArgs connectionEventArgs; // 2

    public DeviceListenerRunnable(int portNumber, IConnectionEventArgs connectionEventArgs) {
        this.portNumber = portNumber;
        serverSocket = new ServerSocket(portNumber);  // 3
        this.connectionEventArgs = connectionEventArgs; 
    }

    @Override
    public void run() {
        Socket socket;
        while (!Thread.currentThread().isInterrupted()) {
            try {
                socket = serverSocket.accept();   // 4
                connectionEventArgs.onDeviceConnected(socket, socket.getRemoteSocketAddress().toString());  // 5
                if (!socket.isClosed())
                    new ReceiverThread(socket, connectionEventArgs).start();  // 6
            } catch (Exception ignored) {
            }
        }
    }
}

Here’s what’s going on inside the code:

  1. Port number for ServerSocket.
  2. An interface which notifies the caller that the new smart meter has connected. We’ll see the implementation of IConnectionEventArgs in a couple of minutes.
  3. Creates the ServerSocket with the specified port number.
  4. Waiting for a new client (Smart Meter) to become online.
  5. Sending the Socket and info of connected smart meter back to the caller class.
  6. Creating a new separate thread for the connected smart meter.

Examining the ReceiverThread class

The ReceiverThread class basically processing all the incoming data from the connected smart meter and send data back to caller class via IConnectionEventArgs interface. Every smart meter connected to an application has its own instance of this class. So, if there are a thousand devices connected to the server than it means every device has it’s own instance of ReceiverThread class because every device communicates in a separate thread. Let’s see the implementation of this class.

public class ReceiverThread extends Thread {
    
    private final Closeable closeable;
    private final IConnectionEventArgs connectionEventArgs;  

    public ReceiverThread(@Nonnull Closeable closeable, IConnectionEventArgs connectionEventArgs) {
        this.closeable = closeable;
        this.connectionEventArgs = connectionEventArgs;
    }

    @Override
    public void run() {
        if (closeable instanceof Socket) {
            Socket socket = (Socket) closeable;
            while (!isInterrupted() && !socket.isClosed()) { // 1
                String info = socket.getRemoteSocketAddress().toString();
                try {
                    byte[] data = handleTcp(socket);  // 2
                    connectionEventArgs.onDataReceived(info, socket, data)  // 3
                } catch (IOException e) {
                    connectionEventArgs.onDeviceDisconnected(info, e);  // 4
                    try {
                        interrupt();   // 5
                        socket.close();   // 6
                    } catch (IOException ignored) {
                    }
                    break;
                }
            }
        }
    }

    private byte[] handleTcp(Socket socket) throws IOException {
        // Logic for reading the input stream 
    }
}

Let’s take a brief look at different steps:

  1. Nothing out of ordinary happens simply start a while loop until the current thread is not interrupted or socket is closed.
  2. Handling the incoming TCP data from a smart meter. (If someone want to see the logic behind handleTcp data method just ask in the comment section)
  3. Sending the incoming data back to caller class. (We’ll see the caller class in a couple of minutes)
  4. If IOException occurs, notify the caller class that the smart meter has been disconnected.
  5. Needs to call the interrupt method on the current thread.
  6. Close socket for the smart meter.

Before going any further I think you guys need to see the legendary IConnectionEventArgs interface implementation.

interface IConnectionEventArgs {

     void onDeviceConnected(Socket socket, String info);

     void onDeviceDisconnected(String info, IOException e);

     void onDataReceived(String info, Closeable c, byte[] data); 
}

Implementation of caller class

We got the basics down now we just need to implement the IConnectionEventArgs interface and start the thread for ServerSocket class. So, the DeviceListenerRunnable class listens to new incoming connections from smart meters.

public class ParentMediaHelper implements IConnectionEventArgs {

    private final Thread listenerThread;
    private final DeviceCollection deviceCollection = DeviceCollection.instance();

    public ParentMediaHelper(int portNumber) {
        DeviceListenerRunnable meterListenerRunnable = new DeviceListenerRunnable(portNumber, this); 
        listenerThread = new Thread(meterListenerRunnable);
    }

    public void startListener() { 
        listenerThread.start();  
    }

    @Override
    public void onDeviceConnected(Socket socket, String info) {
        // handle new smart meter connection 
    }

    @Override
    public void onDeviceDisconnected(String info, IOException e) {
        deviceCollection.remove(info) 
    }

    @Override
    public void onDataReceived(String info, Closeable c, byte[] data) {
         if(containsSerialNumber(data)){ 
              String serialNumber = extractSerialNumber(data)
              deviceCollection.insert(Device(info, c, serialNumber))
         }
         // handle other things
    }
}

I know, many of you think that there are thousands of smart meter connected to the server. How can I determine that the data came from which smart meter? Actually when the first time smart meter sends the data (current or voltage) after a successful socket connection. It also sends its serial number (unique identifier) and with that serial number, I create a new Device model and insert the model in my DeviceCollection class. The DeviceCollection simply a singleton class performs the CRUD functionality.

Similarly when the onDeviceDisconnected calls we remove the device from our collection.

Running the Application Server

Nice! all the hard work is done. Now I just need the main function which simply calls the startListener method inside the ParentMediaHelper class.

public class ddssd {

    private static final int PORT_NUMBER = 14001; // Always use port above 1024

    public static void main(String[] args) {
        ParentMediaHelper mediaHelper = new ParentMediaHelper(PORT_NUMBER);
        mediaHelper.startListener();
    }
}

After running the above program, my application starts accepting connection from the smart meter.

Note: If you’re running application on Linux based OS you need to open the specified port.

Alright, guys, this was my story of creating a Java-based server application. If you like this article please share it with the community.

What’s Next

Implementation of above example with OpenJDK Project Loom, a light-weight concurrency framework.

Thank you for being here and keep reading…

Similar Posts

2 Comments

  1. @Miu Yes you’re right there’s no comparison of .net core with java API services. But this article is only on show how to connect multiple clients in Java. The real application that I built is on Kotlin with coroutines. Also in Kotlin application instead of creating new thread for every device I create a coroutine.

  2. I work for a company with over 3M customers and at least that many smart meters. We chose to do it UDP, rxjava with a store and forward model built on Kafka. With 10 servers, handled the whole load of 1.5M/s.

    Then we moved to .net core and now we’re handling the entire load (3M/s) simultaneously with 3 servers and a par identical stack.

Comments are closed.