Nội dung text Last Minutes Preparations.pdf
What is Socket? explain in details about client/socket options Sockets are fundamental to network programming. They represent a communication endpoint between two applications, allowing them to exchange data over a network. Think of a socket as a virtual door that connects two programs, enabling them to "talk" to each other. In Java, socket options can be configured using the setOption() and getOption() methods of the Socket class (or its related classes like ServerSocket). Some options can be applied before or after a connection is established. Here's a detailed breakdown of the most commonly used socket options: 1) SO_TIMEOUT (Socket Timeout): Sets a timeout for reading data from the input stream. If the timeout expires and no data is read, a SocketTimeoutException is thrown. Eg: socket.setSoTimeout(5000); // Set timeout to 5 seconds int timeout = socket.getSoTimeout(); 2) SO_LINGER (Linger Option): Controls the linger time for a socket when it is closed. If SO_LINGER is enabled, the socket will wait for the specified amount of time before closing. This gives time to send any remaining data. Eg: socket.setSoLinger(true, 10); // Enable linger with 10 seconds timeout boolean isLinger = socket.getSoLinger(); 3) SO_REUSEADDR (Reuse Address): Allows a socket to bind to a port that was previously used by a socket in the TIME_WAIT state. This option is useful for fast restarts. Eg: socket.setReuseAddress(true); // Allow reuse of address boolean canReuse = socket.getReuseAddress(); 4) SO_RCVBUF (Receive Buffer Size): Sets the size of the receive buffer for the socket, which holds incoming data. The size affects how much data can be buffered before it is processed. Eg : socket.setReceiveBufferSize(1024 * 8); // Set buffer size to 8KB int rcvBufSize = socket.getReceiveBufferSize(); 5) SO_SNDBUF (Send Buffer Size): Sets the size of the send buffer for the socket, which holds data to be sent out. This is useful for tuning socket performance, especially in high-throughput applications. Eg: socket.setSendBufferSize(1024 * 8); // Set send buffer size to 8KB int sndBufSize = socket.getSendBufferSize(); 6) SO_KEEPALIVE (Keep-Alive): Enables keep-alive probes for the socket. If enabled, periodic probes are sent to the remote end to ensure the connection is still alive. Eg: socket.setKeepAlive(true); // Enable keep-alive boolean keepAlive = socket.getKeepAlive(); write about socket address and proxy servers. A socket address in networking refers to a combination of an IP address and a port number that uniquely identifies a specific socket on a machine. It acts as an endpoint for communication between devices over a network, allowing systems to send and receive data to and from each other. Key Components of a Socket Address: 1) IP Address: The IP address is the logical identifier for a network interface. It can be either an IPv4 address (e.g., 192.168.1.1) or an IPv6 address (e.g., 2001:db8::ff00:42:8329). 2) Port Number: The port number is a 16-bit integer used to identify a specific process or service on a machine. HTTPS uses port 443. 3) Transport Layer Protocol: The socket can be associated with different transport layer protocols like TCP (Transmission Control Protocol) or UDP (User Datagram Protocol). import java.net.InetAddress; import java.net.InetSocketAddress; public class SocketAddressExample { public static void main(String[] args) throws Exception { InetAddress ipAddress = InetAddress.getByName("example.com"); int port = 80; // Create a socket address InetSocketAddress socketAddress = new InetSocketAddress(ipAddress, port); // Print the socket address System.out.println("IP Address: " + socketAddress.getAddress()); System.out.println("Port: " + socketAddress.getPort()); } } A proxy server is an intermediary between a client and a server that handles requests from the client and forwards them to the appropriate server. Proxy servers are often used to improve security, control network traffic, and enhance performance in network communication. Types of Proxy Servers:1) Forward Proxy: A forward proxy is used by clients to send their requests to a server. The client connects to the proxy, and the proxy forwards the request to the destination server. 2) Reverse Proxy: A reverse proxy is positioned in front of one or more servers and intercepts client requests, routing them to the appropriate server. The client interacts with the proxy, which forwards the request to the server. 3) Transparent Proxy: A transparent proxy sits between a client and a server but operates in a way that is invisible to the client. The client’s traffic is intercepted without any configuration changes on the client side. import java.net.*; import java.io.*; public class ProxyServerExample { public static void main(String[] args) throws IOException { // Define the proxy address and port Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)); // Create a socket and connect through the proxy Socket socket = new Socket(proxy); socket.connect(new InetSocketAddress("example.com", 80)); System.out.println("Connected to example.com through proxy."); socket.close(); } } what do you know about half closed sockets? A half-closed socket refers to a state in network communication where one end of a TCP connection has stopped sending data but can still receive data from the other end. This is a feature of the Transmission Control Protocol (TCP) that allows for a graceful shutdown of the connection while continuing communication in one direction. Understanding Half-Closed Sockets: 1) Full Duplex Communication: A typical TCP connection allows data to flow in both directions simultaneously. When both ends are sending and receiving data, this is called full-duplex communication. 2) Half-Close: The TCP connection allows either the client or the server to signal that it is done sending data by sending a FIN (Finish) packet to the other side, indicating the half-closure. The connection is still maintained, allowing the other side to continue sending data if needed. How Half-Closed Sockets Work: 1) Sending a FIN Packet: When one side decides that it has finished sending data, it sends a FIN packet to the other side. This action indicates that it won’t send any more data but is still open to receiving data. 2) Receiving the FIN: The other side receives the FIN packet, indicating that the connection is now half-closed. It can still send data to the half- closed socket but cannot expect to receive any more data from it. 3) Closing the Other Half: Once the other side finishes sending its data, it also sends a FIN packet, fully closing the connection. 4) Connection Teardown: After both sides have sent and received FIN packets, the connection is closed in both directions, and no more data can be sent or received. import java.net.*; import java.io.*; public class HalfClosedSocketExample { public static void main(String[] args) throws IOException { // Connect to a server Socket socket = new Socket("example.com", 80); // Send data to the server OutputStream outputStream = socket.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream, true); writer.println("GET / HTTP/1.1"); writer.println("Host: example.com"); writer.println(); // Half-close the socket: stop sending data, but keep receiving socket.shutdownOutput(); // Read the server's response InputStream inputStream = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String responseLine; while ((responseLine = reader.readLine()) != null) { System.out.println(responseLine); } // Fully close the socket socket.close(); }} write a program to look for the ports on the local machine by using server socket objects. Java program that scans ports on the local machine: PortInUseScanner.java import java.io.IOException; import java.net.ServerSocket; public class PortScanner { public static void main(String[] args) { int startPort = 1; // Starting port number int endPort = 65535; // Ending port number System.out.println("Scanning for open ports on the local machine..."); for (int port = startPort; port <= endPort; port++) { try { // Try to bind to the port ServerSocket serverSocket = new ServerSocket(port); serverSocket.close(); // Close the socket if binding succeeds } catch (IOException e) { // If we catch an exception, the port is already in use System.out.println("Port " + port + " is in use."); } } System.out.println("Port scanning completed.");}} To print both available and in-use ports: PortAvailabilityScanner.java import java.io.IOException; import java.net.ServerSocket; public class PortScanner { public static void main(String[] args) { int startPort = 1; int endPort = 1024; // Standard well-known ports System.out.println("Scanning for open ports on the local machine..."); for (int port = startPort; port <= endPort; port++) { try { // Try to bind to the port ServerSocket serverSocket = new ServerSocket(port); serverSocket.close(); // Close the socket if binding succeeds System.out.println("Port " + port + " is available."); } catch (IOException e) { System.out.println("Port " + port + " is in use."); } } System.out.println("Port scanning completed."); }} explain in details about the server socket options Server sockets in Java (and other programming languages) provide several configuration options that control how they behave in terms of connection management, timeouts, performance, and security. For Server Sockets, Java supports Three options: 1) SO_TIMEOUT (Socket Timeout): Sets the timeout for how long a server socket will wait for a client connection before giving up. This option is crucial in situations where the server might need to wait for a client connection, but you don't want the server to wait indefinitely. Eg : ServerSocket serverSocket = new ServerSocket(8080); serverSocket.setSoTimeout(5000); // 5 seconds timeout try { Socket clientSocket = serverSocket.accept(); // Wait for a connection } catch (SocketTimeoutException e) { System.out.println("Connection timed out!"); } 2) SO_REUSEADDR (Reuse Address): Allows a socket to bind to an address that is in the TIME_WAIT state. This option is useful when you need to restart a server quickly, and the previous instance of the server hasn't fully released the port due to TCP's TIME_WAIT state (which can last for several minutes). By enabling SO_REUSEADDR, the new instance can bind to the same port without waiting for the previous connection to fully close. Eg: ServerSocket serverSocket = new ServerSocket(); serverSocket.setReuseAddress(true); // Enable reuse of address serverSocket.bind(new InetSocketAddress(8080)); 3) SO_RCVBUF (Receive Buffer Size): Specifies the size of the buffer used by the underlying operating system to store incoming data for a socket. This option is used to control the amount of memory allocated for receiving incoming data before it is passed to the application. Larger buffer sizes can improve throughput in high-bandwidth environments, but they also consume more system memory. Eg: ServerSocket serverSocket = new ServerSocket(8080); serverSocket.setReceiveBufferSize(8192); // 8 KB buffer What is multithreaded servers? Show with examples A multithreaded server is a server that can handle multiple client requests concurrently by creating separate threads for each client connection. This allows the server to manage multiple clients at the same time, instead of processing one client request at a time (which is typical of single-threaded servers). Server Code (MultithreadedServer.java) import java.io.*; import java.net.*; public class MultithreadedServer { public static void main(String[] args) { int port = 8080; try (ServerSocket serverSocket = new ServerSocket(port)) { System.out.println("Server listening on port " + port); while (true) { Socket clientSocket = serverSocket.accept(); new ClientHandler(clientSocket).start(); // Create new thread for each client } } catch (IOException e) { e.printStackTrace(); }}} class ClientHandler extends Thread { private Socket clientSocket; public ClientHandler(Socket socket) { this.clientSocket = socket; } @Override public void run() { try (BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter output = new PrintWriter(clientSocket.getOutputStream(), true)) { String message; while ((message = input.readLine()) != null) { System.out.println("Client: " + message); output.println("Server: " + message); // Echo back to client if ("bye".equalsIgnoreCase(message)) break; } } catch (IOException e) { e.printStackTrace(); } finally { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }}
Write about the overload createsocket() methods to build a SSL Socket. The createSocket() methods are part of the javax.net.ssl.SSLSocketFactory class, which is used to create secure sockets for communication over SSL (Secure Sockets Layer) or TLS (Transport Layer Security) protocols in Java. The SSLSocketFactory class provides several overloaded createSocket() methods, allowing developers to create SSL sockets with different configurations for secure communication. Common Overloaded createSocket() Methods in SSLSocketFactory: 1) createSocket() (No Arguments): Creates an unconnected SSL socket. Once created, it can be connected later using connect() method, or it can be configured with additional properties. Eg: SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(); 2) createSocket(String host, int port) (Connect to a Specific Host and Port): Creates and connects the SSL socket to a specified remote host and port. This is the most commonly used form when establishing an SSL connection to a server. Eg: SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket("example.com", 443); 3) createSocket(InetAddress address, int port) (Connect to a Specific IP Address and Port): Similar to the previous overload, but uses an InetAddress object instead of a string hostname to specify the remote server. Eg: InetAddress address = InetAddress.getByName("example.com"); SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(address, 443); 4) createSocket(String host, int port, InetAddress localAddr, int localPort) (Connect to a Host with Local Address and Port Binding): Creates an SSL socket, connects it to the specified remote host and port, and binds it to a specified local address and port. Eg: InetAddress localAddr = InetAddress.getByName("192.168.1.100"); SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket("example.com", 443, localAddr, 12345); 5) createSocket(InetAddress address, int port, InetAddress localAddr, int localPort) (Connect to IP Address with Local Binding): Similar to the previous overload but accepts an InetAddress for both the remote and local addresses instead of a string hostname. Eg: InetAddress remoteAddr = InetAddress.getByName("example.com"); InetAddress localAddr = InetAddress.getByName("192.168.1.100"); SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(remoteAddr, 443, localAddr, 12345); 6) createSocket(Socket socket, String host, int port, boolean autoClose) (Layer SSL on an Existing Socket): This overload is used to layer SSL/TLS on top of an existing plain socket. It is useful when upgrading an existing connection (like a regular Socket) to an SSL connection after some initial communication (such as a STARTTLS negotiation). Eg: Socket plainSocket = new Socket("example.com", 80); SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(plainSocket, "example.com", 443, true); 7) createSocket(Socket socket, InputStream consumed, boolean autoClose) (SSL Layering with Consumed InputStream): This method allows SSL to be layered on top of an existing Socket connection where part of the input stream has already been consumed. Eg: InputStream consumedInputStream = socket.getInputStream(); // Assume part of the stream is read SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(plainSocket, consumedInputStream, true); What should we consider for configuring SSL Server Socket? Explain When configuring an SSL Server Socket in Java, you need to consider several critical aspects to ensure secure and efficient communication. Configuring an SSL server socket involves setting up cryptographic elements like certificates, key stores, and protocols to allow secure connections from SSL/TLS clients. 1) Key Store Configuration: A key store contains the server’s private keys and the certificates needed to authenticate itself to clients. The server uses the private key to establish SSL/TLS connections. 2) Trust Store Configuration: The trust store contains certificates from trusted Certificate Authorities (CAs) that the server uses to validate client certificates (if mutual authentication is enabled). 3) SSL Protocol Selection: SSL/TLS protocols define the security standards for communication. The most commonly used protocols are TLS 1.2 and TLS 1.3 (as SSL 3.0 and older protocols are considered insecure). 4) Cipher Suites: A cipher suite is a combination of algorithms used to secure communication, including key exchange, encryption, and message authentication. 5) Client Authentication (Optional): one way and two way authentication 6) Session Management: SSL/TLS sessions are often reused to avoid the overhead of renegotiating security parameters for each connection. 7) Security Updates and Patches: The cryptographic libraries and protocols used by SSL/TLS can have vulnerabilities. 8) Socket Timeout Configuration: Set timeouts for socket operations to prevent Denial of Service (DoS) attacks where clients can tie up server resources by keeping sockets open. import javax.net.ssl.*; import java.io.*; import java.security.KeyStore; public class SSLServer { public static void main(String[] args) throws Exception { KeyStore keyStore = KeyStore.getInstance("JKS"); try (FileInputStream keyStoreFile = new FileInputStream("serverkeystore.jks")) { keyStore.load(keyStoreFile, "password".toCharArray()); } KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, "password".toCharArray()); SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(keyManagerFactory.getKeyManagers(), null, null); SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(8443); sslServerSocket.setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1.3"}); sslServerSocket.setEnabledCipherSuites(new String[] { "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" }); System.out.println("SSL Server started, waiting for connections..."); while (true) { SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); System.out.println("Client connected"); BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream())); PrintWriter out = new PrintWriter(sslSocket.getOutputStream(), true); out.println("Hello from SSL Server!"); System.out.println("Received: " + in.readLine()); sslSocket.close(); } }} List down the UDP features and write short notes on DatagramSocket and DatagramPacket Class Features of UDP: 1) Connectionless: UDP does not require a reliable connection to be established before data transmission. This makes it more efficient for applications that don't need guaranteed delivery or order of packets. 2) Datagram-based: UDP transmits data in discrete units called datagrams. Each datagram is independent and can be lost or arrive out of order without affecting other datagrams. 3) Best-effort delivery: UDP does not guarantee that datagrams will be delivered or that they will arrive in the correct order. If a datagram is lost or corrupted, there is no mechanism to resend it. 4) Lightweight: UDP has a smaller overhead compared to TCP, making it suitable for applications that prioritize speed over reliability. 5) No flow control or congestion control: UDP does not implement flow control or congestion control mechanisms, which can lead to network congestion if the sender sends data too quickly. a) DatagramSocket Class : The DatagramSocket class in Java is used to create a socket for sending and receiving datagram packets over the network using UDP. Unlike TCP's Socket, which is connection-oriented, DatagramSocket supports connectionless communication, allowing it to send and receive packets independently. DatagramSocket: Creating a socket: The DatagramSocket class is used to create a UDP socket. It provides methods for sending and receiving datagrams. Binding to a port: A DatagramSocket object must be bound to a specific port number on the local machine. This allows the socket to receive incoming datagrams addressed to that port. Sending datagrams: The send method of the DatagramSocket class is used to send a datagram to a specified destination address and port. Receiving datagrams: The receive method of the DatagramSocket class is used to receive an incoming datagram. It returns a DatagramPacket object containing the received data and the sender's address. Example of Creating a DatagramSocket: DatagramSocket socket = new DatagramSocket(1234); // Binds to port 1234 b) DatagramPacket Class:The DatagramPacket class in Java represents a data packet to be sent or received via a DatagramSocket over UDP. A datagram packet is a self-contained, independent packet of data that carries enough information for the receiver to understand where it originated and what data it contains. Holds Data: It contains the actual data to be transmitted as a byte array. Packet Addressing: It includes information about the destination IP address and port number for outgoing packets, and it can store the sender's IP address and port for incoming packets. Used for Both Sending and Receiving: The same DatagramPacket class is used to send and receive data. For sending, you specify the destination address, and for receiving, the packet will store the sender's information. Size of Packet: You must specify the length of the packet's data. UDP datagrams are limited to a size of 65,535 bytes, but most networks limit packet sizes further (often around 1,500 bytes). Example of Creating a DatagramPacket: byte[] data = "Hello".getBytes(); //For Sending InetAddress address = InetAddress.getByName("192.168.1.1"); DatagramPacket packet = new DatagramPacket(data, data.length, address, 1234); byte[] buffer = new byte[1024]; // For receiving DatagramPacket packet = new DatagramPacket(buffer, buffer.length); Discuss RMI architecture with suitable diagram and discuss each layer in the architecture briefly. Java Remote Method Invocation (RMI) enables an object running in one Java Virtual Machine (JVM) to invoke methods on an object running in another JVM. This mechanism facilitates remote communication between applications and is based on the concept of distributed objects. The RMI architecture is layered to allow for easy communication, object marshalling/unmarshalling, and transparent method invocation. Layers of RMI Architecture (Stub/Skeleton Layer, Remote Reference Layer, Transport Layer) 1) Stub/Skeleton Layer: a) Stub: A local object that represents the remote object on the client side. It acts as a proxy for the remote object, handling method calls and marshalling/ unmarshalling of parameters and return values. b) Skeleton: A local object that represents the remote object on the server side. It receives incoming method calls from the stub, unmarshalls the parameters, invokes the actual method on the remote object, and marshals the return value. 2. Remote Reference Layer: This layer manages references to remote objects and handles the communication between the stub and the skeleton. It provides (Invocation Handling, Reference Management, Garbage Collection) 3) Transport Layer: Responsible for transmitting data between the client and server. Typically uses TCP/IP for reliable, connection-oriented communication. Handles tasks like establishing connections, sending and receiving data, and closing connections. How RMI Architecture Works: a) Client-Side: The client invokes a method on the local stub.The stub serializes the method parameters and sends the invocation request to the server over the transport layer. b) Server-Side: The request is passed through the transport layer to the skeleton (or directly to the remote object if skeletons are not used).The skeleton deserializes the parameters and invokes the actual method on the remote object.The result is serialized and sent back to the client. c) Return to Client: The client stub receives the response, deserializes the result, and returns the value to the client program. Write a code to create RMI Client and Server to add two numbers and display the summed up result. Server.java import java.rmi.Naming; import java.rmi.registry.LocateRegistry; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; interface Adder extends java.rmi.Remote { int add(int x, int y) throws RemoteException; } class AdderRemote extends UnicastRemoteObject implements Adder { protected AdderRemote() throws RemoteException { super(); } @Override public int add(int x, int y) throws RemoteException { return x + y; } }public class Server { public static void main(String[] args) { try { LocateRegistry.createRegistry(1099); System.out.println("RMI registry started."); AdderRemote adder = new AdderRemote(); Naming.rebind("rmi://localhost:1099/AdderService", adder); System.out.println("AdderService is bound and ready to use."); } catch (Exception e) { e.printStackTrace(); } } } Client.java import java.rmi.Naming; public class Client { public static void main(String[] args) { try { Adder stub = (Adder) Naming.lookup("rmi://localhost:1099/AdderService"); int result = stub.add(5, 3); System.out.println("Result of addition: " + result); } catch (Exception e) { e.printStackTrace(); } } }