Network Programming with Java: A Comprehensive Guide

Network Programming with Java

Strengths of Java for Network Programming

  • Platform Independence: Runs on any OS without modification.
  • Rich Libraries: java.net supports networking tasks easily.
  • Multithreading: Handles multiple clients efficiently.
  • Security: Inbuilt features like SSL/TLS for secure communication.
  • Automatic Memory Management: Reduces memory leak risks.

Weaknesses of Java

  • Performance: Slower due to JVM overhead.
  • Low-Level Control: Less flexibility for low-level network tasks.
  • Memory Usage: High memory consumption due to the JVM.
  • Latency: Unpredictable delays from garbage collection.
  • JVM Dependency: Java requires the JVM to be installed on the host machine.

InetAddress

Syntax

import java.net.*;

InetAddress obj = InetAddress.getByName("www.google.com");
System.out.println(obj);

Methods of InetAddress Class

Factory Methods
  • getByName(String host): Creates an InetAddress object based on the provided hostname.
  • getByAddress(byte[] addr): Returns an InetAddress object from a byte array of the raw IP address.
  • getAllByName(String host): Returns an array of InetAddress objects from the specified hostname, as a hostname can be associated with several IP addresses.
  • getLocalHost(): Returns the address of the localhost.
Getter Methods
  • getHostAddress(): Returns the IP address in string format.
  • getHostName(): Returns the host name of the IP address.
  • getCanonicalHostName(): Returns the fully qualified domain name for this IP.
  • getAddress(): Returns the dotted quad format of the IP address.

Program to Find Canonical Hostname, Hostname, and IP Address

import java.net.*;

public class App {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress address = InetAddress.getByName("www.xyz.com.np");
        System.out.println(address.getCanonicalHostName());
        System.out.println(address.getHostAddress());
        System.out.println(address.getHostName());
        byte[] arr = address.getAddress();
        for (byte b : arr) {
            System.out.println(b);
        }
    }
}

Program to Retrieve IP and MAC Address

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;

public class IPMacAddress {
    public static void main(String[] args) {
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            System.out.println("IP Address: " + inetAddress.getHostAddress());
            NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
            byte[] macAddress = networkInterface.getHardwareAddress();
            StringBuilder macString = new StringBuilder();
            for (int i = 0; i 

The NetworkInterface Class

The NetworkInterface class, part of the java.net package, represents a network interface in the local machine.

Features of NetworkInterface Class

  1. Retrieve Interfaces: getByName(), getByInetAddress(), and getNetworkInterfaces() for getting network interfaces by name, IP, or listing all interfaces.
  2. IP Addresses: getInetAddresses() returns all IP addresses of the interface.
  3. MAC Address: getHardwareAddress() returns the MAC address as a byte array.
  4. Interface Name: getName() for interface name and getDisplayName() for a readable name.
  5. getMTU(): Returns the maximum packet size.

Java Program to Split Different Components of a URL

public static void main(String[] args) throws MalformedURLException {
    URL url1 = new URL("https://www.example.com:8080/path/to/resource?key1=value1&key2=value2#section2");
    System.out.println(url1.toString());
    System.out.println();
    System.out.println("Different components of the URL1-");
    System.out.println("Protocol:- " + url1.getProtocol());
    System.out.println("Hostname:- " + url1.getHost());
    System.out.println("Default port:- " + url1.getDefaultPort());
    System.out.println("Query:- " + url1.getQuery());
    System.out.println("Path:- " + url1.getPath());
    System.out.println("File:- " + url1.getFile());
    System.out.println("Reference:- " + url1.getRef());
}

Java Program to Get Different Parts of a URI

import java.net.*;

public class App {
    public static void main(String[] args) throws Exception {
        String str = "http://www.xml.com/pub/a/2003/09/17/stax.html#id=_hbc";
        URI uri = URI.create(str);
        System.out.println(uri.normalize().toString());
        System.out.println("Scheme = " + uri.getScheme());
        System.out.println("Schemespecificpart = " + uri.getSchemeSpecificPart());
        System.out.println("Raw User Info = " + uri.getFragment());
        System.out.println("User Info = " + uri.getUserInfo());
        System.out.println("Authority = " + uri.getAuthority());
        System.out.println("Host = " + uri.getHost());
        System.out.println("Path = " + uri.getPath());
        System.out.println("Port = " + uri.getPort());
        System.out.println("Query = " + uri.getQuery());
    }
}

Program to Retrieve Data from a URL Using URLConnection

package com.javatpoint;

import java.net.*;

public class URLExample {
    public static void main(String[] args) {
        try {
            URL url = new URL("https://www.javatpoint.com/java-tutorial");
            System.out.println("Protocol: " + url.getProtocol());
            System.out.println("Host Name: " + url.getHost());
            System.out.println("Port Number: " + url.getPort());
            System.out.println("File Name: " + url.getFile());
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

package com.javatpoint;

import java.net.*;
import java.io.*;

public class UrlConnectionReader {
    public static void main(String[] args) {
        String output = getUrlContents("https://www.javatpoint.com/javatutorial");
        System.out.println(output);
    }

    private static String getUrlContents(String theUrl) {
        StringBuilder content = new StringBuilder();
        try {
            URL url = new URL(theUrl);
            URLConnection urlConnection = url.openConnection();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                content.append(line + "\n");
            }
            bufferedReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content.toString();
    }
}

Program to Handle HTTP Cookies

import java.net.*;
import java.util.*;

public class App {
    private final static String URL_STRING = "[Insert URL Here]"; // Replace with your URL

    public static void main(String[] args) throws Exception {
        CookieManager cookieManager = new CookieManager();
        CookieHandler.setDefault(cookieManager);
        URL url = new URL(URL_STRING);
        URLConnection connection = url.openConnection();
        connection.getContent();
        CookieStore cookieStore = cookieManager.getCookieStore();
        List<HttpCookie> cookieList = cookieStore.getCookies();
        for (HttpCookie cookie : cookieList) {
            System.out.println("Domain: " + cookie.getDomain());
            System.out.println("max age: " + cookie.getMaxAge());
            System.out.println("name of cookie: " + cookie.getName());
            System.out.println("server path: " + cookie.getPath());
            System.out.println("is cookie secure: " + cookie.getSecure());
            System.out.println("value of cookie: " + cookie.getValue());
            System.out.println("value of cookie version: " + cookie.getVersion());
        }
    }
}

HTTP Request Methods

The Hypertext Transfer Protocol (HTTP) enables communication between clients and servers. HTTP works as a request-response protocol.

  • GET: Retrieves information from the server using a given URI.
  • HEAD: Same as GET, but transfers only the status line and header section.
  • POST: Sends data to the server (e.g., customer information, file uploads).
  • PUT: Replaces all current representations of the target resource with the uploaded content.
  • DELETE: Removes all current representations of the target resource given by a URI.

Example

GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

Server Response:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Vary: Authorization,Accept
Accept-Ranges: bytes
Content-Length: 88
Content-Type: text/html
Connection: Closed

<h1>Hello, World!</h1>

Program to Fetch Website Content Using URLConnection

import java.io.*;
import java.net.*;

public class App {
    public static void main(String[] args) throws Exception {
        StringBuilder content = new StringBuilder();
        try {
            URL u = new URL("https://google.com/contact-us/");
            URLConnection uc = u.openConnection();
            InputStream in = uc.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                content.append(line + "\n");
            }
            bufferedReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(content);
    }
}

Web Cache

A web cache stores copies of web resources to reduce server load and speed up content delivery. When a request is made for a cached resource, the web cache can serve the stored copy instead of fetching it from the original server.

Common Directives Used with the Cache-Control Header

  • public: Indicates the response can be cached by any cache.
  • private: Specifies that the response is specific to a single user and should not be cached by shared caches.
  • no-cache: Forces a request to be sent to the server to revalidate the content before using a cached version.
  • no-store: Prevents any caching of the response.
  • max-age=: Specifies the maximum time (in seconds) the resource is considered fresh and can be cached.

Program to Display Socket Information

import java.net.*;
import java.io.*;

public class SocketInfo {
    public static void main(String args[]) {
        try {
            Socket thesocket = new Socket("www.google.com", 80);
            System.out.println("Connected to " + thesocket.getInetAddress() + " on port " + thesocket.getPort() + " from port " + thesocket.getLocalPort() + " of " + thesocket.getLocalAddress());
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

// Output: Connected to www.google.com/142.250.67.36 on port 80 from port 54873 of /192.168.100.7

Socket Programming

Socket programming enables data communication between computers across a network using either a connection-oriented or connectionless protocol. We’ll use TCP/IP, a connection-oriented protocol. The client must know the server’s IP address and port number.

Reading from Server Program

MyClient.java
import java.io.*;
import java.net.*;

public class MyClient {
    public static void main(String[] args) throws IOException {
        Socket clientSocket = new Socket("localhost", 1234);
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String serverResponse = in.readLine();
        System.out.println("Server says: " + serverResponse);
        clientSocket.close();
    }
}
MyServer.java
import java.io.*;
import java.net.*;

public class MyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(1234);
        Socket clientSocket = serverSocket.accept();
        System.out.println("Connected to Client");
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        out.println("Hello from server");
        clientSocket.close();
        serverSocket.close();
    }
}

Writing to Server with Socket

MyClient.java
import java.io.*;
import java.net.*;

public class MyClient {
    public static void main(String[] args) throws IOException {
        Socket client = new Socket("localhost", 1234);
        PrintWriter out = new PrintWriter(client.getOutputStream(), true);
        out.println("Hello from client");
        client.close();
    }
}
MyServer.java
import java.io.*;
import java.net.*;

public class MyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(1234);
        System.out.println("Server started");
        Socket server = serverSocket.accept();
        BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
        String clientMessage = in.readLine();
        System.out.println("Client says: " + clientMessage);
        server.close();
        serverSocket.close();
    }
}

Daytime Service Using Socket

MyServer.java
import java.io.*;
import java.net.*;
import java.util.*;

public class MyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(1234);
        Socket clientSocket = serverSocket.accept();
        System.out.println("Connected to Client");
        String currentDateTime = new Date().toString();
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        out.println(currentDateTime);
        clientSocket.close();
        serverSocket.close();
    }
}
MyClient.java
import java.io.*;
import java.net.*;

public class MyClient {
    public static void main(String[] args) throws IOException {
        Socket clientSocket = new Socket("localhost", 1234);
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String serverResponse = in.readLine();
        System.out.println("Server says: " + serverResponse);
        clientSocket.close();
    }
}

Two-Way Operation Between Client and Server

MyClient.java
import java.io.*;
import java.net.*;

public class MyClient {
    public static void main(String[] args) throws IOException {
        Socket clientSocket = new Socket("localhost", 1234);
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        int a = 5;
        int b = 6;
        out.println(a + " " + b);
        String serverResponse = in.readLine();
        System.out.println("Sum from server: " + serverResponse);
        clientSocket.close();
    }
}
MyServer.java
import java.io.*;
import java.net.*;

public class MyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(1234);
        Socket clientSocket = serverSocket.accept();
        System.out.println("Connected to Client");
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String inputLine = in.readLine();
        System.out.println("Received from client: " + inputLine);
        String[] numbers = inputLine.split(" ");
        int a = Integer.parseInt(numbers[0]);
        int b = Integer.parseInt(numbers[1]);
        int sum = a + b;
        System.out.println("Calculated sum: " + sum);
        out.println(sum);
        clientSocket.close();
        serverSocket.close();
    }
}

Life Cycle of a Server Program

  • A new ServerSocket is created on a port using a ServerSocket() constructor.
  • ServerSocket listens on the port using the accept() method.
  • Uses getInputStream() and/or getOutputStream().
  • Server and client interact using an agreed-upon communication protocol.
  • Either the server or the client (or both) close the connection.
  • The server goes back to listening with the accept() method.

HaHKnPk8RRFUaKJb2cO1O75djJpO3SwthIO1l5Ccu2bby8Pp8070LYHWs75WMY6PsmV+1BlzgiypLBXFEVRlKxE8qqUoiiKoihxgwp7RVEURYlzVNgriqIoSpyjwl5RFEVR4hwV9oqiKIoS56iwVxRFUZQ4R4W9oiiKosQ5KuwVRVEUJc5RYa8oiqIocY4Ke0VRFEWJc1TYK4qiKEqco8JeURRFUeIcFfaKoiiKEueosFcURVGUOEeFvaIoiqLEOSrsFUVRFCXOUWGvKIqiKHGOCntFURRFiXNU2CuKoihKnKPCXlEURVHiHBX2iqIoihLnqLBXFEVRlDhHhb2iKIqixDkq7BVFURQlzlFhryiKoihxjgp7RVEURYlzVNgriqIoSpyjwl5RFEVR4hwV9oqiKIoS56iwVxRFUZQ4R4W9oiiKosQ1xvwfOVs+O9ss0f8AAAAASUVORK5CYII=

Procedure for Constructing and Connecting Server Sockets

  1. Create a Server Socket
  2. Wait for Client Connections
  3. Establish I/O Streams
  4. Handle Client Communication
  5. Close the Connection

Example

import java.io.*;
import java.net.*;

public class SimpleServer {
    public static void main(String[] args) {
        int port = 1234; // Server will listen on this port
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Server is listening on port " + port);
            Socket clientSocket = serverSocket.accept();
            System.out.println("Client connected");
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            String clientMessage = in.readLine();
            System.out.println("Received: " + clientMessage);
            out.println("Hello, Client!");
            clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Creating a Secure Client Socket

import javax.net.ssl.*;
import java.net.*;

public class App {
    public static void main(String[] args) throws Exception {
        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        Socket socket = factory.createSocket("127.0.0.1", 7000); // Corrected IP address
        System.out.println("Server Connected " + socket); // connected
        socket.close();
    }
}

Creating a Secure Server Socket

import javax.net.ssl.*;
import java.net.*;

public class App {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = ((SSLServerSocketFactory) SSLServerSocketFactory.getDefault()).createServerSocket(7000);
        Socket socket = serverSocket.accept();
        System.out.println(socket + " Client Accepted"); // connected
        socket.close();
    }
}

Blocking vs. Non-Blocking IO

Blocking I/O

  1. Based on the Blocking I/O operation
  2. Stream-oriented
  3. Channels are not available
  4. Selectors are not available
  5. Blocking IO waits for data to be written or read before returning.

Non-Blocking I/O

  1. Based on the Non-blocking I/O operation
  2. Buffer-oriented
  3. Channels are available
  4. Selectors are available
  5. Non-blocking IO does not wait for data to be read or written before returning.

URL Connection

URL Connection is an abstract class whose subclasses link the user application and web resources. It enables reading and writing to resources referenced by a URL object.

Basic Steps to Use URL Connection

  • Create a URL
  • Retrieve the URL Connection object
  • Set output capability on the URL connection
  • Open a connection to the resource
  • Get an output stream from the connection
  • Write to the output stream
  • Close the output stream

Program to Illustrate Buffer and Channel

Writing Data to a File

import java.io.*;
import java.nio.*;

public class FileChannelWrite {
    public static void main(String[] args) {
        File outputFile = new File("hello.txt");
        String text = "Hello world.";
        try {
            FileOutputStream fos = new FileOutputStream(outputFile);
            FileChannel fileChannel = fos.getChannel();
            byte[] bytes = text.getBytes();
            ByteBuffer buffer = ByteBuffer.wrap(bytes);
            fileChannel.write(buffer);
            fileChannel.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }
    }
}

Reading Data from a File

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileChannelRead {
    public static void main(String[] args) {
        File inputFile = new File("hello.txt");
        if (!inputFile.exists()) {
            System.out.println("The input file doesn't exist.");
            return;
        }
        try {
            FileInputStream fis = new FileInputStream(inputFile);
            FileChannel fileChannel = fis.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (fileChannel.read(buffer) > 0) {
                buffer.flip();
                while (buffer.hasRemaining()) {
                    byte b = buffer.get();
                    System.out.print((char) b);
                }
                buffer.clear();
            }
            fileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Simple UDP Client

  • Import Required Classes
  • Create DatagramSocket
  • Prepare the Message
  • Create DatagramPacket
  • Send the Packet
  • Close the Socket

Simple Program

import java.net.*;

public class App {
    public static void main(String[] args) throws Exception {
        DatagramSocket clientSocket = new DatagramSocket();
        String message = "Hello, UDP Server!";
        byte[] sbuffer = message.getBytes();
        InetAddress serverAddress = InetAddress.getByName("localhost");
        DatagramPacket sendPacket = new DatagramPacket(sbuffer, sbuffer.length, serverAddress, 5000);
        clientSocket.send(sendPacket);
        clientSocket.close();
    }
}

UDPServer.java

import java.net.*;

public class UDPServer {
    public static void main(String[] args) {
        try {
            DatagramSocket serverSocket = new DatagramSocket(5000);
            byte[] receiveBuffer = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
            System.out.println("Server is waiting for a packet...");
            serverSocket.receive(receivePacket);
            String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
            System.out.println("Received: " + receivedMessage);
            serverSocket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Multicast Sockets

The MulticastSocket class is used for sending and receiving IP multicast packets. It’s a UDP DatagramSocket with added capabilities for joining groups of other multicast hosts. A multicast group is specified by a Class D IP address (224.0.0.0 to 239.255.255.255) and a standard UDP port number. When a message is sent to a multicast group, all subscribing recipients receive it. The socket doesn’t need to be a group member to send messages. When a socket subscribes, it receives datagrams sent by other hosts to the group, like other members. A socket leaves a group using the leaveGroup(InetAddress addr) method. Multiple MulticastSockets can subscribe concurrently and receive group datagrams.

RMI Architecture

Remote Method Invocation (RMI) allows an object to invoke a method on an object in another address space (same or remote machine). An object in one JVM can invoke methods on an object in another JVM. RMI creates a public remote server object for client-server communication through method calls.

Layers

  • Transport Layer: Connects the client and server, manages connections.
  • Stub: Client-side proxy of the remote object, acts as a gateway.
  • Skeleton: Server-side object that the stub communicates with.
  • RRL (Remote Reference Layer): Manages client references to the remote object.

Working of an RMI Application

  • The client’s call to the remote object is received by the stub and passed to the RRL.
  • The client-side RRL invokes invoke() on remoteRef, passing the request to the server-side RRL.
  • The server-side RRL passes the request to the Skeleton, which invokes the required object on the server.
  • The result is returned to the client.

Goals of RMI

− • To minimize the complexity of the application. • To preserve type safety. • Distributed garbage collection. •Minimize the difference between working with local and remote objects

Why logging is important in a program? What do you log and how to log?
The log data provides a comprehensive picture of what is happening in real time. In addition to recording start-ups and shutdowns, commands executed, login and logoff information, established connections, and a lot more, it also records events such as commands executed. Log data is the footprints of a network, which is why it is called log data. The use of logs has been essential for troubleshooting application and infrastructure performance for many years. In addition, they provide insight into how our applications are running on the various infrastructure components, helping us to improve our applications. An exception to the memory or hard disk can be found in log data.