Java Socket Programming
Have you ever built a chat application using Java Socket Programming🤔? Read this article to understand how to do that.
Table of contents
No headings in the article.
First of all, what is a Java Socket? It is simply an endpoint of communication for two machines on the network wherein we have a client and a server. So first of all let us understand what client-server architecture is. (I have started from the basics of the network that might be handy here and then moved on to the main application deployment)
The client sends a request to the server and the server responds with a service or operation for example, our server contains the data or files and the client requests for a particular file so, in that case, the server will process the data and respond by providing the requested file. Taking a more simple approach the server can perform some mathematical operations and the client requests for the addition of 2 numbers, in this case, the server will respond by providing the added result of those two numbers.
Now let's move on to understanding what protocols are. Protocols are simply set of rules that govern how two computers communicate with each other over a network. The data from one computer travels to another computer in the form of data packets. There are some specific formats in which the data packets are to be sent and received by the computers. This data format can be specified through the protocols. We will mainly talk about TCP/IP protocol here.
It is used between the web client and the web server. Also, the important point here is that TCP/IP protocol is a full duplex communication protocol which means we can use the same connection link to send as well as receive the packets. The protocol also takes care of damaged and lost packets which is not the same in the case of UDP. If it is the case of video or media transfer, we can use UDP because even if some information is lost in transmission, that's not much of an issue. But in the case of text messages, if any part of the information is lost, the whole meaning of the text might change from the sender to the receiver side. This is why we use TCP/IP in the case of the chat application.
Now talking about IP Addresses. It is the unique number by which any machine is identified on the network. For our chat application, we will also need to bind the server socket object to a port number. So, the port number is basically on which a particular application of a system runs. When a machine tries to contact another machine, it requires two things which are IP Address and Port Number.
Now the basics are over, let's come to the main stuff. We'll start by building a simple calculator application wherein there is a client and a server. The client requests a particular mathematical operation and the server responds with the result.
Talking more about sockets. We will need to import the Socket class for creating a socket object to enable implementing all the fundamental socket operations like sending and reading data and also closing connections. Apart from this, we need to import the ServerSocket class which is used to implement the server sockets for a client/server socket connection.
Server Side Code:
import java.io.*; //importing all the classes required for the Input or Output operations
import java.io.IOException;//importing the IOException class
import java.net.ServerSocket;//importing the ServerSocket class
import java.net.Socket;// importing the Socket class
import java.io.BufferedReader;//importing the BufferedReader class
//The .io package contains all the classes for input and output operations and
//.net package contains all the classes to implement networking applications
public class Cal_Server
{
public static void main(String[] args) throws Exception
{
System.out.println();
System.out.println("The Server has started");
System.out.println();
ServerSocket ss=new ServerSocket(9806); //Creating a ServerSocket object with a port number
System.out.println("Waiting for the client.....");
System.out.println();
Socket soc=ss.accept(); // Accepting the client request
System.out.println("The connection is established between with the client");
System.out.println();
BufferedReader in=new BufferedReader(new InputStreamReader(soc.getInputStream()));// Reading data from client
PrintWriter out=new PrintWriter(soc.getOutputStream(), true); // Writing data to client
while(true)
{
String str[]=in.readLine().split(":");// split into different parts based on the delimeter storing each part in string array
int option=Integer.parseInt(str[0]); // converting the string to integer
int num1=Integer.parseInt(str[1]);
int num2=Integer.parseInt(str[2]);
String result="";
int flag=0;
switch(option)
{
case 1:
result="Addition is:"+(num1+num2);
break;
case 2:
result="Subtraction is:"+(num1-num2);
break;
case 3:
result="Multiplication is:"+(num1*num2);
break;
case 4:
result="Divison is:"+(num1/(double)num2);
break;
case 5:
flag=1;
break;
default:
break;
}
if(flag==1)
{
break;
}
out.println(result);
}
System.out.println();
System.out.println("The Server has terminated");
}
}
And here is the client-side code:
import java.io.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.BufferedReader;
public class Cal_Client
{
public static void main(String[] args) throws Exception
{
System.out.println();
System.out.println("The client has started...");
Socket soc = new Socket("localhost", 9806);//Creating a socket object with an IP address and a port number
BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));
BufferedReader in = new BufferedReader(new InputStreamReader(soc.getInputStream())); // Reading data from client
PrintWriter out= new PrintWriter(soc.getOutputStream(), autoFlush:true); // true for auto flush
//The auto flush indicates that whether the buffered out put should be automatically flushed when filled or an exception should be raised to indicate the buffer overflow
int ch, num_1, num_2;
do
{
System.out.println("Please choose the operation from the list of options");
System.out.println("1. Addition");
System.out.println();
System.out.println("2. Subtraction");
System.out.println();
System.out.println("3. Multiplication");
System.out.println();
System.out.println("4. Divison");
System.out.println();
System.out.println("5. Exit");
System.out.println();
ch=Integer.parseInt(userInput.readLine());
if(ch==5)
{
out.println(ch+":0:0");
break;
}
else
{
System.out.println("Please enter the first number: ");
num_1=Integer.parseInt(userInput.readLine());
System.out.println();
System.out.println("Please enter the second number: ");
num_2=Integer.parseInt(userInput.readLine());
out.println(ch+":"+num_1+":"+num_2);
String answer=in.readLine();
System.out.println("Server says: "+answer);
}
}while(true);
System.out.println("The client has terminated.");
}
}
The codes contain comments wherever needed. Run the server first in a different terminal then run the client in another terminal just like shown in the picture.
Now let us move on to the main stuff which is the Chat Application. To make it easier for the readers to understand the concepts involved in the code, I have added comments to the code wherever necessary.
The server-side logic:
//This is a multi user chat application
//A multi threaded chat server
import java.io.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.io.BufferedReader;
public class Server
{
static ArrayList<String> userNames=new ArrayList<String>(); //List to store all the usernames of the clients associated with our server
static ArrayList<PrintWriter> printWriters=new ArrayList<PrintWriter>(); // List to store the PrintWriter objects of all the clients
public static void main(String[] args) throws Exception
{
System.out.println();
System.out.println("Waiting for the clients....");
System.out.println();
ServerSocket ss = new ServerSocket(9806);// Creating a server socket object and binding it with a port number
while(true)// While loop is for the server to continuously wait for the incoming client connections
{
Socket soc = ss.accept();// The accept method will accept the incoming client connections and return a socket object
//That socket object will be caught in soc
System.out.println("The connection is established between the client and the server.");
System.out.println();
ConversationHandler handler = new ConversationHandler(soc); // Creating the object of conversation handler inside which we will pass the soc socket object
handler.start(); //Calling the start method to start the thread
}
}
}
class ConversationHandler extends Thread // Creating a thread class
{
Socket socket; // The socket for which we are creating a thread
BufferedReader in;
PrintWriter out;
String name;
PrintWriter pw;
static FileWriter fw;
static BufferedWriter bw;
public ConversationHandler(Socket socket) throws IOException
{
this.socket = socket;
fw=new FileWriter("C:\\Users\\hp\\ChatServerLogs.txt",true);// We use true so as to just append the content and not overwrite it
bw=new BufferedWriter(fw);// The BufferedWriter is capable of writing a whole string at once unlike FileWriter
pw=new PrintWriter(bw,true);
}
public void run()
{
try
{
in=new BufferedReader(new InputStreamReader(socket.getInputStream())); // The BufferedReader will be used to read the data from the socket's input stream
out = new PrintWriter(socket.getOutputStream(), true); //The PrintWriter will be used for sending the data to the socket's output stream
int count=0;
while(true) // A while loop to wait till the user enters a unique name
{
if(count>0)
{
out.println("NAMEALREADYEXISTS");
}
else
{
out.println("NAMEREQUIRED");
}
name=in.readLine(); // To capture the name from the client side
if(name==null)
{
return;
}
if(!Server.userNames.contains(name)) // If the particulat user name is not present in the array list
{
Server.userNames.add(name); // Then add that name to the list
break; // Once we get a unique username, we break the loop
}
count++;
}
out.println("NAMEACCEPTED"+name);
Server.printWriters.add(out); // Adding that name inside the printWriters array list
while(true)
{
String message=in.readLine();// Reading the message from the socket's input stream
if(message==null)
{
return;
}
//We need to send the message from client to all the other clients
pw.println(name +":"+message);// Writing to the file
for(PrintWriter writer:Server.printWriters)
{
writer.println(name + ":" +message); // appending the message with the name
}
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
The client-side code:
//This is a multi user chat application
import java.io.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.FlowLayout;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.BufferedReader;
//All the Swing components will be static because we are using the same components for all our client instances
public class Client
{
static JFrame chatWindow = new JFrame("Chat Application");// Creating a JFrame object and basically it is the outer frame on which we are going to add our components
static JTextArea chatArea=new JTextArea(22,40);// A chat area to display all our messages
//It takes two parameters that is number of rows and number of columns
static JTextField textField = new JTextField(20);// This text field will be used for entering our messages
//It takes one parameter and that is only the number of columns
static JLabel blankLabel=new JLabel(" ");//The blankLabel is used to display a blank space between the text field and the chat area
static JButton sendButton=new JButton("Send");//Send button
static BufferedReader in;
static PrintWriter out;
static JLabel nameLabel=new JLabel(" ");
Client()
{
//Setting up some components and properties of our chatWindow
chatWindow.setLayout(new FlowLayout());// Using FlowLayout to arrange the components on out JFrame in the required manner
chatWindow.add(nameLabel);// For displaying the name of the current user
chatWindow.add(new JScrollPane(chatArea));// Adding a scroller functionality for our chat area
chatWindow.add(blankLabel);// Adding the blankLabel
chatWindow.add(textField);
chatWindow.add(sendButton);//Adding the send button
chatWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// Adding close method to close the application as soon as the user clicks on the close button
chatWindow.setSize(475,500);// Setting the size of our chat window
chatWindow.setVisible(true);// If we do not create this, the messages will not be displayed on our chat window
textField.setEditable(false);// Once the server permits the user to send messages then only this text field should be set to true
chatArea.setEditable(false);// the chat area will only be used for displaying the messages and not for entering the messages
sendButton.addActionListener(new Listener()); //Binding the Listener with the send button
textField.addActionListener(new Listener()); //User can also send a message using Enter key
}
void startChat() throws Exception
{
String ipAddress =JOptionPane.showInputDialog( // This method will beused for displaying the dialog box
chatWindow, //component on which we display the dialog box
"Enter IP Address", //Message to be displayed on the dialog box
"IP Address Required!!", //The title of the dialog box
JOptionPane.PLAIN_MESSAGE); //The type of message to be displayed
//PLAIN_MESSAGE is a constant which is a constant of the class JOptionPane
Socket soc= new Socket(ipAddress,9806); // Creating a socket object
in=new BufferedReader(new InputStreamReader(soc.getInputStream())); //BufferedReader object
out= new PrintWriter(soc.getOutputStream(), true); //PrintWriter object
while(true)
{
String str=in.readLine();
if (str.equals("NAMEREQUIRED"))
{
String name = JOptionPane.showInputDialog(
chatWindow,
"Enter a unique name:",
"Name Required!!",
JOptionPane.PLAIN_MESSAGE);
out.println(name);
}
else if(str.equals("NAMEALREADYEXISTS"))
{
String name=JOptionPane.showInputDialog(
chatWindow,
"Enter another name:",
"Name already Exists!!",
JOptionPane.WARNING_MESSAGE);
out.println(name);
}
else if(str.startsWith("NAMEACCEPTED"))
{
textField.setEditable(true); // Setting the text field editable
nameLabel.setText("You are logged in as: "+ str.substring(12)); //Till the index number 11 we have NAMEACCEPTED and after that we have name from server
}
else
{
chatArea.append(str+ "\n"); // If non of the other three messages, the message will be considered as a normal message from the clients
}
}
}
public static void main(String[] args) throws Exception
{
Client client = new Client();// Creating the Client object in the main method
client.startChat(); //Calling startChat function
}
}
//The code which needs to be executed when the send button is clicked
class Listener implements ActionListener //Class to implement some,istener interface
{
public void actionPerformed(ActionEvent e)
{
Client.out.println(Client.textField.getText()); //Whatever the user types in, it will be sent to socket's output stream
Client.textField.setText(""); //Clearing the text field
}
}
Just read the code once, try to understand the logic in the first place, google the concepts which might not be clear and then feel free to deploy the codes as separate files and run them separately with the server running first.
Thank you for reading folks✌️! Hope it will help you all.