Access modifiers for classes or interfaces in Java
Because of Java's ecosystem, the language remains first when writing sophisticated applications. Java has a lot of features that make complex tasks simple. We will now develop a functional Java chat program that can operate on several client sockets.
Threads in socket programming
Working with sockets, which helps establish a network connection to the server, is done through socket programming. It functions as a client-server system in which the client contacts the server with a request, and the server replies with the information the client has requested.
We can use the thread class to override the run method to implement the threads in Java socket programming. We can also use the socket class, which requires two inputs, an IP address and a port number to implement the socket.
What are Java chat applications?
We can use a Java programming language to create Java chat programs. Users can communicate in real using chat programs where the exchange of messages will occur. Client-server talks can do this. We can use chat applications for different things, such as communicating with friends and business purposes.
There will be three files in this chat program. Server.Java serves as a provider and represents the chat application's backend. ClientHandler.java, which implements threads, handles all thread operations and allows each message to be executed concurrently. The last Client.Java represents the client's interface and front-end functionality.
What is server-side programming?
An application that runs on a server and is available to clients across any network can be developed using server-side programming. These programs often handle client requests, perform server functions like data fetching, and respond to client requests.
Server-Side Programming (Server.java)
Java programming on the server side maintains a given port for incoming connections and notifies the client when a relationship is maintained. This communicates using the IP address and access port number of the client over a socket.
To create a working server side, we will create a Server.java file that manages all the methods. Sending and receiving messages and forwarding them to the appropriate location is how it operates. Over the network, messages are read and written using an input-output stream. It may use threads to manage several clients at once, or it may use other Java technologies to process requests.
File name: Server.java
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server { private ServerSocket serverSocket; public Server(ServerSocket serverSocket){ this.serverSocket = serverSocket; } public void serverStart(){ try{ while(!serverSocket.isClosed()){ Socket socket = serverSocket.accept(); System.out.println("New Friend Connected"); ClientHandler clientHandler = new ClientHandler(socket); Thread thread = new Thread(clientHandler); thread.start(); } } catch (IOException e){ } } public void closerServer(){ try{ if(serverSocket != null){ serverSocket.close(); } } catch(IOException e){ e.printStackTrace(); } } public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(1234); Server server = new Server(serverSocket); server.serverStart(); } }
Features of server-side programming
One of the many characteristics of server-side programming is its ability to connect and interact with different client servers. With the server providing all the features and resources the client requires, it acts as a source for the handler.
General dynamic data can be maintained more effectively with the help of server-side programming. It gives you control over user interaction by providing people with the necessary information, making it easier to understand. The application's security has been enhanced, and multiple users can log in simultaneously.
Server class
When a client requests to connect, the server class will wait for it and create a new thread to fulfil the request. The ServerScoket object in this class is in charge of listening for new connections or clients and generating a socket object to communicate with them.
It also contains a ClientHandler object, which will be created later. In basic terms, each ClientHandler object in the class will oversee corresponding with a client. In addition, this class will implement the runnable interface.
If no faults are found, closing the connection can be done by using the closeServer() method. It closes the server if the server socket connection is null. Otherwise, it checks for null values and produces an IOException.
ClientHandler class
This class overrides the run method and implements the runnable interface. This class's initial feature is a static ArrayList that contains each client handler object we have created.
This ArrayList's main function is to maintain track of all of our clients so that, if a client sends a message, we may send it to each client by looping over our array list of clients. This is static since it must be a part of the class and not of every individual object.
Data, namely messages to our clients—statements sent by other clients—is sent and received via BufferedReader and BufferedWriter. The ArrayList
mentioned above will then be used to disseminate it.
The start() method, which generates a new thread object, uses DataInputStream and DataOutputStream as objects.
File name: ClientHandler.java
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.ArrayList; public class ClientHandler implements Runnable{ public static ArrayList<ClientHandler>clientHandlers = new ArrayList<>(); public Socket socket; private BufferedReader buffReader; private BufferedWriter buffWriter; private String name; public ClientHandler(Socket socket){ try{ this.socket = socket; this.buffWriter = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())); this.buffReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); this.name = buffReader.readLine(); clientHandlers.add(this); boradcastMessage("SERVER" + name + " has entered in the room"); } catch(IOException e){ closeAll(socket, buffReader, buffWriter); } } @Override public void run() { String messageFromClient; while(socket.isConnected()){ try{ messageFromClient = buffReader.readLine(); boradcastMessage(messageFromClient); } catch(IOException e){ closeAll(socket, buffReader, buffWriter); break; } } } public void boradcastMessage(String messageToSend){ for(ClientHandler clientHandler: clientHandlers){ try{ if(!clientHandler.name.equals(name)){ clientHandler.buffWriter.write(messageToSend); clientHandler.buffWriter.newLine(); clientHandler.buffWriter.flush(); } } catch(IOException e){ closeAll(socket,buffReader, buffWriter); } } } public void removeClientHandler(){ clientHandlers.remove(this); boradcastMessage("server " + name + " has gone"); } public void closeAll(Socket socket, BufferedReader buffReader, BufferedWriter buffWriter){ removeClientHandler(); try{ if(buffReader!= null){ buffReader.close(); } if(buffWriter != null){ buffWriter.close(); } if(socket != null){ socket.close(); } } catch (IOException e){ e.getStackTrace(); } } }
The client handler manages the client entry and exit positions. The declaration that a specific user has left the chat and the statement that a particular user has joined the chat are broadcast to other clients.
To make the server responsive to operations, it features two threads: one for working with the rest of the application and another for waiting for messages.
What is client-side programming?
Applications intended to operate on the client side are created using client-side programming. This indicates that local hosts, as opposed to distant networks, are used to run client-side programs. It is frequently used to build dynamic and interactive client-side user interfaces that are intended to handle complex equations on the server side and carry out various actions.
Programming on the client side only considers the client and should meet its needs. When using client-side programming with server-side programming in Java, the client presents the data interactively while the server manages advanced tasks.
File name: Client.java
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner; public class Client { private Socket socket; private BufferedReader buffReader; private BufferedWriter buffWriter; private String name; public Client(Socket socket, String name){ try{ this.socket = socket; this.buffWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); this.buffReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); this.name = name; }catch (IOException e){ closeAll(socket, buffReader, buffWriter); } } public void sendMessage(){ try{ buffWriter.write(name); buffWriter.newLine(); buffWriter.flush(); try (Scanner sc = new Scanner(System.in)) { while(socket.isConnected()){ String messageToSend = sc.nextLine(); buffWriter.write(name + ": " + messageToSend); buffWriter.newLine(); buffWriter.flush(); } } } catch(IOException e){ closeAll(socket, buffReader, buffWriter); } } public void readMessage(){ new Thread( new Runnable() { @Override public void run() { String msfFromGroupChat; while(socket.isConnected()){ try{ msfFromGroupChat = buffReader.readLine(); System.out.println(msfFromGroupChat); } catch (IOException e){ closeAll(socket, buffReader, buffWriter); } } } }).start(); } public void closeAll(Socket socket, BufferedReader buffReader, BufferedWriter buffWriter){ try{ if(buffReader!= null){ buffReader.close(); } if(buffWriter != null){ buffWriter.close(); } if(socket != null){ socket.close(); } } catch (IOException e){ e.getStackTrace(); } } public static void main(String[] args) throws UnknownHostException, IOException{ try (Scanner sc = new Scanner(System.in)) { System.out.println("Enter your name"); String name = sc.nextLine(); Socket socket = new Socket("localhost", 1234); Client client = new Client(socket, name); client.readMessage(); client.sendMessage(); } } }
Features of client-side programming
Unlike programming on a remote server, client-side programming is executed on the user's computer or devices. The user's device must have all the resources and requirements to run the program.
The main advantages of user clients are their increased performance and interactivity. Because the work is executed locally on the server, initializing the charge is quicker than with server-side programming, allowing users to finish it more quickly.
SendMessage
In simple terms, the connection the server has created to handle a client is the SendMessage() method, which is used to send messages to our client handler. For ClientHandler to recognize them, it requires various input parameters to be enclosed in try-and-catch statements.
The Socket, BufferedReader, and BufferedWriter objects are closed by the code using a method called closeEverything if an IOException arises during the message-sending attempt.
public void sendMessage(){ try{ buffWriter.write(name); buffWriter.newLine(); buffWriter.flush(); Scanner sc = new Scanner(System.in); while(socket.isConnected()){ String messageToSend = sc.nextLine(); buffWriter.write(name + ": " + messageToSend); buffWriter.newLine(); buffWriter.flush(); } } catch(IOException e){ closeAll(socket, buffReader, buffWriter); } }
ReadMessage
The technique for keeping an eye out for messages that have been broadcast is readMessage(). To avoid waiting all of the time for a message and being unable to send one, we used a new thread while we listened to messages. This serves as a blocking action.
It passes the runnable object and accepts a new thread. As long as the plug is attached, it goes into an endless loop. Using a BufferedReader object, the code tries to read a message from the server inside the loop and saves it in a variable named msfFromGroupChat.
The final method is closeEverything, which closes the object if no null value is discovered. It requires three arguments. In the event of an IOException, the object's stack trace will be returned.
public void readMessage(){ new Thread( new Runnable() { @Override public void run() { String msfFromGroupChat; while(socket.isConnected()){ try{ msfFromGroupChat = buffReader.readLine(); System.out.println(msfFromGroupChat); } catch (IOException e){ closeAll(socket, buffReader, buffWriter); } } } }).start(); }
How to run the above program?
Step 1: Open three terminals. We will run the server on the first terminal, and the other two are for clients.
Step 2: Enter the following command to launch the server application in the first terminal. The Java server file will now be found.
Step 3: In the remaining two terminals, type this command to run clients.
Step 4: Type in each client's username separately.
Step 5: Involve in meaningful discussion by starting up a conversation.
Client-1
Client-2