Click here to Skip to main content
15,923,051 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
How to make our sender-server-receiver Java code into a client-server mail exchange code that does the follwoing:
Each client can send and receive simultaneously (i.e. multi-threading): using multithreading, each client should act as a receiver waiting for incoming emails while being able to send new emails at the same time. In other words, both functionalities should work at the same time.

Please I need help on how to make the multi-threaded server so multiple clients can send emails at once.

What I have tried:

Server code


Java
<pre>import java.io.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Server{
	
	//store the received valid email
	private static final Set<String> validEmails = new HashSet<>(); // Set to store validated email addresses
	private static final Map<String, InetAddress> emailToAddressMap = new HashMap<>(); // Hashmap to associate email addresses with their corresponding hostname "localhost"
    private static final Map<String, Integer> emailToPortMap = new HashMap<>(); // Hashmap to keep track of email addresses and their corresponding ports
    //private static int expectedSequenceNumber = 1; // Keeps track of the expected sequence number for messages.
    private static final int BUFFER_SIZE = 1024;

	
	public static void main(String[] args) {
		
		DatagramSocket serverSocket = null; //server socket
		preSavedEmails(); // initialize the pre-saved emails
		loadEmailToHostNameMap(); // initialize the server with a mapping of email addresses to hostnames
		
		try {
			
			//initialize server socket
			serverSocket = new DatagramSocket(12346);
			//byte to store and receive data
			
			//print server information
			String serverHostName = InetAddress.getLocalHost().getHostName();
			System.out.println("Mail server starting at host: " + serverHostName );
			System.out.println("Waiting to be contacted for transferring Mail.. ");
			System.out.println();
            StringBuilder messageBuilder = new StringBuilder();

			
			while (true) {
				byte[] receiveData = new byte[BUFFER_SIZE];
				
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                serverSocket.receive(receivePacket); // Receive packet from client

                ClientHandler clientHandler = new ClientHandler(serverSocket, receivePacket);
                Thread clientThread = new Thread(clientHandler);
                clientThread.start();
				
				
				// Receiving messages from the client
				//DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				serverSocket.receive(receivePacket);
				String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
                String segment = new String(receivePacket.getData(), 0, receivePacket.getLength()).trim();
				String[] parts = message.split("_");
				
				String timestamp = java.time.LocalDateTime.now().toString();//get timestamp
				InetAddress clientAddress = receivePacket.getAddress(); //gets the client address
				int clientPort = receivePacket.getPort(); //gets the client port

				
				 // Handle initial connection request
                if (message.equals("Connect")) {
                    System.out.println("Connection request received from " + clientAddress + ": " + clientPort);
                    // Send ACK to client after receiving Connect
                    send(serverSocket, clientAddress, "ACK", clientPort);
                    System.out.println("Sent ACK to client " + clientAddress + ": " + clientPort);
                    continue; // Skip the rest of the loop to wait for the next packet
                } else if (message.equals("ACK ACK")) {
                    System.out.println("Connection established with " + clientAddress + ": " + clientPort);
    				System.out.println();
                    continue; // Wait for the actual email messages to come through
                }
                else if (message.equals("TERMINATE_CONNECTION")) { // If the received message is a termination 
                    System.out.println();
                	System.out.println("Termination request received from " + clientAddress + ": " + clientPort);
                	// Send "ACK" in response to "TERMINATE_CONNECTION"
                    send(serverSocket, clientAddress, "ACK", clientPort);
                	System.out.println("Sent ACK to client " + clientAddress + ": " + clientPort + " for termination request.");
                	
                	// Wait for final "ACK ACK" from the sender
                	DatagramPacket finalAckAckPacket = new DatagramPacket(new byte[1024], 1024); 
                	serverSocket.receive(finalAckAckPacket);
                	String finalAckAck = new String(finalAckAckPacket.getData(), 0, finalAckAckPacket.getLength());
                	if (finalAckAck.equals("ACK ACK")) { //If received ACK ACK terminate connection
                		System.out.println("Final ACK ACK received from " + clientAddress + ": " + clientPort + ". Connection terminated.");
                	} else {
                		System.out.println("Unexpected message received after termination request: " + finalAckAck);
                	}
                	continue; // Continue listening for new connections or messages
                } else {
                    if (segment.contains("END_EMAIL")) {
                        messageBuilder.append(segment.replace("END_EMAIL", ""));
                        processAndForwardEmail(serverSocket, messageBuilder.toString(), clientAddress, clientPort);
                        messageBuilder.setLength(0);
                    } else {
                        messageBuilder.append(segment);
                    }
                }

 
			}
			
			// check the server socket to see if it is open and close it
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (serverSocket != null && !serverSocket.isClosed()) {
				serverSocket.close();
			}
		}
	}
	
    private static void send(DatagramSocket socket, InetAddress address, String message, int port) throws IOException {
        byte[] data = message.getBytes();
        DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
        socket.send(packet);
    }

    private static String receive(DatagramSocket socket) throws IOException {
        byte[] data = new byte[1024];
        DatagramPacket packet = new DatagramPacket(data, data.length);
        socket.receive(packet);
        return new String(packet.getData(), 0, packet.getLength());
    }
    
	
	// method for the set of pre-saved emails
	private static void preSavedEmails() {
		validEmails.add(" client1@example.com");
		validEmails.add(" client2@example.com");
		validEmails.add(" client3@example.com");
		validEmails.add(" client4@example.com");
		validEmails.add(" client5@example.com");
	}
	
	// method to check if an email is in the set of valid emails
	private static boolean verifyEmail(String email) {
		return validEmails.contains(email);
	}
	
	
	//method for mapping emails to host name
	// it preloads mappings of email addresses to their host addresses and port numbers
	private static void loadEmailToHostNameMap() {
        try {
			// mapping email addresses to hostname
            emailToAddressMap.put("client1@example.com", InetAddress.getByName("localhost"));
            // mapping email addresses to port number
			emailToPortMap.put("client1@example.com", 12347);
            
            emailToAddressMap.put("client2@example.com", InetAddress.getByName("localhost"));
            emailToPortMap.put("client2@example.com", 12347);
            
            emailToAddressMap.put("client3@example.com", InetAddress.getByName("localhost"));
            emailToPortMap.put("client3@example.com", 12347);
            
            emailToAddressMap.put("client4@example.com", InetAddress.getByName("localhost"));
            emailToPortMap.put("client4@example.com", 12347);
            
            emailToAddressMap.put("client5@example.com", InetAddress.getByName("localhost"));
            emailToPortMap.put("client5@example.com", 12347);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
	
	
	private static void forwardEmailToReceiver(DatagramSocket serverSocket, String mailTo, String message) throws IOException {
	    if (emailToAddressMap.containsKey(mailTo) && emailToPortMap.containsKey(mailTo)) {
	        InetAddress receiverAddress = emailToAddressMap.get(mailTo);
	        int receiverPort = emailToPortMap.get(mailTo);
	        String fullMessage = message + "END"; // Append "END" to the message
	        byte[] forwardData = fullMessage.getBytes(); 
	        DatagramPacket forwardPacket = new DatagramPacket(forwardData, forwardData.length, receiverAddress, receiverPort);
	        serverSocket.send(forwardPacket);
	        System.out.println("Email forwarded to " + mailTo);
	        System.out.println();
	    } else {
	        System.out.println("Email to " + mailTo + " could not be forwarded. Receiver not found.");
	        System.out.println();
	    }
	}

	

    private static void processAndForwardEmail(DatagramSocket serverSocket, String fullMessage, InetAddress clientAddress, int clientPort) throws IOException {
        System.out.println("Processing email received from " + clientAddress);
        
        // Extract details from the email content
        Map<String, String> emailDetails = parseEmailContent(fullMessage);
        String formattedEmail = fullMessage.replace('_', '\n');
        System.out.println("Email Content: \n" + formattedEmail);
        System.out.println();
        saveEmail(emailDetails);
        
        // Check if attachment is present and call saveAttachment with sender and receiver
        if (emailDetails.containsKey("ATTACHMENT")) {
            String sender = emailDetails.get("FROM");
            String receiver = emailDetails.get("TO");
            String attachmentInfo = emailDetails.get("ATTACHMENT");
            saveAttachment(sender, receiver, attachmentInfo);
        }
        
        String recipientEmail = emailDetails.get("TO"); 
        if (recipientEmail != null) {
            forwardEmailToReceiver(serverSocket, recipientEmail, fullMessage);
        } else {
            System.out.println("No valid recipient found in the email details.");
        }    
        }
    

    private static Map<String, String> parseEmailContent(String emailContent) {
        Map<String, String> details = new HashMap<>();
        String[] parts = emailContent.split("_");
        for (String part : parts) {
            int colonIndex = part.indexOf(':');
            if (colonIndex > -1) {
                String key = part.substring(0, colonIndex);
                String value = part.substring(colonIndex + 1);
                details.put(key, value);
            }
        }
        return details;
    }
    
    private static void saveEmail(Map<String, String> emailDetails) throws IOException {
        // Create directories for sender and receiver
        String senderDirectory = "./ServerEmails/" + emailDetails.get("FROM") + "/sent";
        String receiverDirectory = "./ServerEmails/" + emailDetails.get("TO") + "/inbox";

        // Ensure directories exist
        new File(senderDirectory).mkdirs();
        new File(receiverDirectory).mkdirs();

        // Define file names
        String timestamp = String.valueOf(System.currentTimeMillis());
        String subjectFormatted = emailDetails.get("SUBJECT").replace(" ", "_");
        String emailFileName = subjectFormatted + "_" + timestamp + ".txt";

        // File paths
        File senderFile = new File(senderDirectory, emailFileName);
        File receiverFile = new File(receiverDirectory, emailFileName);

        // Save email to both sender and receiver directories
        saveEmailToFile(emailDetails, senderFile);
        saveEmailToFile(emailDetails, receiverFile);
    }

    private static void saveEmailToFile(Map<String, String> emailDetails, File file) throws IOException {
        try (PrintWriter out = new PrintWriter(new FileWriter(file))) {
            out.println("From: " + emailDetails.get("FROM"));
            out.println("To: " + emailDetails.get("TO"));
            out.println("Subject: " + emailDetails.get("SUBJECT"));
            out.println("Body:\n" + emailDetails.get("BODY"));
        }
        System.out.println("Email saved to: " + file.getAbsolutePath());
    }



    private static void saveAttachment(String sender, String receiver, String attachmentInfo) throws IOException {
        // Parse the filename and the Base64 encoded data
        int firstColon = attachmentInfo.indexOf(':');
        String fileName = attachmentInfo.substring(0, firstColon);
        String base64Data = attachmentInfo.substring(firstColon + 1);
        byte[] data = Base64.getDecoder().decode(base64Data);

        // Directories for attachment
        String senderAttachmentDir = "./ServerEmails/" + sender + "/sent/attachments";
        String receiverAttachmentDir = "./ServerEmails/" + receiver + "/inbox/attachments";

        // Ensure directories exist
        new File(senderAttachmentDir).mkdirs();
        new File(receiverAttachmentDir).mkdirs();

        // File paths
        File senderAttachmentFile = new File(senderAttachmentDir, fileName);
        File receiverAttachmentFile = new File(receiverAttachmentDir, fileName);

        // Save attachment files
        saveAttachmentToFile(data, senderAttachmentFile);
        saveAttachmentToFile(data, receiverAttachmentFile);
    }

    private static void saveAttachmentToFile(byte[] data, File file) throws IOException {
        try (FileOutputStream out = new FileOutputStream(file)) {
            out.write(data);
        }
        System.out.println("Attachment saved to: " + file.getAbsolutePath());
    }

}


class ClientHandler implements Runnable {
	     
	//Declare instance variables for the ClientHandler class
	DatagramSocket serverSocket;
	private DatagramPacket receivePacket;
	private DatagramPacket sendPacket;
	private InetAddress clientAddress = null;
	private int clientPort = 0;
	private byte[] receiveData;
	private byte[] sendData;
	private int serverPort;
	
	//Declare instance variables for the email
	String IP_address, Transfer_Type, File_Name, Msg;
	byte[] Body_Data;
	int Sequence_Num = 0;
	int ACK_Num = 0;
	long bLength;
	String[] header;
	
	
    private static final int BUFFER_SIZE = 1024;

    public ClientHandler(DatagramSocket serverSocket, DatagramPacket receivePacket) {
        this.serverSocket = serverSocket;
        this.receivePacket = receivePacket;
        this.clientAddress = receivePacket.getAddress();
        this.clientPort = receivePacket.getPort();
    }

    @Override
    public void run() {
        try {
        	// Displaying the client that is running
            System.out.println("Client " + Thread.currentThread().getId());
            handleClientRequest();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handleClientRequest() throws IOException {
    	
        String message = new String(receivePacket.getData(), 0, receivePacket.getLength());

        if ("Connect".equals(message)) {
            System.out.println("Connection request received from " + clientAddress + ":" + clientPort);
            send("ACK");
            System.out.println("Sent ACK to client " + clientAddress + ":" + clientPort);
        } else if ("TERMINATE_CONNECTION".equals(message)) {
            System.out.println("Termination request received from " + clientAddress + ":" + clientPort);
            send("ACK");
            System.out.println("Sent ACK to client " + clientAddress + ":" + clientPort + " for termination request.");
        } else {
            // Handle other requests
            System.out.println("Received unknown request from client " + clientAddress + ":" + clientPort + ": " + message);
        }
        
    }

    //to send packets
    private void send(String message) throws IOException {
        byte[] sendData = message.getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientAddress, clientPort);
        serverSocket.send(sendPacket);
    }
    
}






Sender/client code


Java
<pre>import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Sender {
    
	public static void main(String[] args) {
		
        DatagramSocket clientSocket = null; // DatagramSocket used for sending and receiving datagram packets
        int sequenceNumber = 1; // Sequence number to keep track of each email sent
        
        try{
            clientSocket = new DatagramSocket(); // new DatagramSocket instance for the client
            InetAddress serverAddress = InetAddress.getByName("localhost"); // Resolves the hostname "localhost" to an IP address which is used as the server's address
            Scanner scanner = new Scanner(System.in);
            System.out.println("Mail client starting on host: " + InetAddress.getLocalHost().getHostName());
            System.out.println();
            
            serverAddress = null;

            while (serverAddress == null) {
                System.out.print("Type name of Mail server: ");
                String serverName = scanner.nextLine();
                
                try {
                    serverAddress = InetAddress.getByName(serverName);
                } catch (UnknownHostException e) {
                    System.out.println("Server name is not available. Please enter a valid server name.");
                    // Continue looping if the server name is not resolved
                    continue;
                }
            }
            
            System.out.println("Initiating connection to the mail server. Sending Connect...");
            byte[] initData = "Connect".getBytes(); // Converts the string "Connect" into a byte array
            
            // Creates a DatagramPacket with the "Connect" message sending it to port 12346
            DatagramPacket initPacket = new DatagramPacket(initData, initData.length, serverAddress, 12346);
            
            clientSocket.send(initPacket); // Sends the intial Connect packet to the server
            byte[] ackData = new byte[1024]; // Allocates a byte array to hold the server's response
            DatagramPacket ackPacket = new DatagramPacket(ackData, ackData.length); // Receiving the server's acknowledgment ACK
            clientSocket.receive(ackPacket); // Waits for and receives the acknowledgment packet from the server
            String ackMsg = new String(ackPacket.getData(), 0, ackPacket.getLength()); // Creates a new String from the received response ACK
            if (!"ACK".equals(ackMsg.trim())) { //Checks if the received message is not "ACK"
                System.out.println("Failed to connect to the server. Exiting...");
                return;
            }
            else {
                System.out.println("Received ACK from the server.");
            }
            System.out.println("Sending ACK ACK to the server..."); // Sending another acknowledgement ACK ACK
            byte[] ackAckData = "ACK ACK".getBytes(); // Converts the "ACK ACK" message into a byte array
            // Creates a DatagramPacket with the "ACK ACK" message sending it to port 12346
            DatagramPacket ackAckPacket = new DatagramPacket(ackAckData, ackAckData.length, serverAddress, 12346);
            clientSocket.send(ackAckPacket); // Sends the ACK ACK packet to the server completing the handshake process
            System.out.println("Connected successfully to the server.");
            System.out.println();
 
                
            while (true) {
            	
            	//giving the option to sync inbox, create email, or terminate connection
            	Scanner option = new Scanner(System.in);
            	System.out.println("ENTER AN OPTION TO PROCEED:");
            	System.out.println("[1] Sync inbox");
            	System.out.println("[2] Create an email");
            	System.out.println("[3] Terminate Connection");
            	
            	int option_of_client = option.nextInt();
            	
				// OPTION 1: SYNC INBOX ----------------------------------
				if (option_of_client == 1) {

					// ask client for their email address to sync the
					// inbox associated to them.
					Scanner console = new Scanner(System.in);

					System.out.println("Enter your email address to proceed: ");

					String clientEmail = console.nextLine();

					// Sending sync inbox request to the server
					System.out.println("Syncing inbox...");
					String syncRequest = "SYNC_INBOX";
					byte[] syncRequestData = syncRequest.getBytes();
					DatagramPacket syncPacket = new DatagramPacket(syncRequestData, syncRequestData.length,
							serverAddress, 12346);
					clientSocket.send(syncPacket);

					// Receive acknowledgment from the server
					byte[] syncAckData = new byte[1024];
					DatagramPacket syncAckPacket = new DatagramPacket(syncAckData, syncAckData.length);
					clientSocket.receive(syncAckPacket);
					String syncAckMsg = new String(syncAckPacket.getData(), 0, syncAckPacket.getLength());
					if ("ACK".equals(syncAckMsg.trim())) {
						System.out.println("Inbox sync request acknowledged by the server.");
					} else {
						System.out.println("Failed to sync inbox. Server response: " + syncAckMsg);
					}
				}
            	
                //OPTION 2: CREATE NEW EMAIL ----------------------------
                else if(option_of_client == 2) {
                	System.out.println("\nCreating New Email...");
               
                	// Getting email input from the user
                	String mailTo = getEmailInput(scanner, "To", "@example.com");
                	String mailFrom = getEmailInput(scanner, "From", "@example.com");
                	String subject = getNonEmptyInput(scanner, "Subject");
                	String body = getNonEmptyInput(scanner, "Body");
                
                	// Creating a string representing the email header with the sequence number
                	String emailHeader = "TO_" + mailTo + "_FROM_" + mailFrom + "_SUBJECT_" + subject + "_BODY_" + body + "_SEQ_" + sequenceNumber;
                	byte[] emailHeaderData = emailHeader.getBytes(); // Converts the email header string into a byte array
                	saveEmailToFile(sequenceNumber, emailHeader, null); // Saves email headers to a file; attachment is handled separately
                
                	// Send the email header
                	DatagramPacket sendPacket = new DatagramPacket(emailHeaderData, emailHeaderData.length, serverAddress, 12346);
                	clientSocket.send(sendPacket);
                	System.out.println("Email sent with sequence number " + sequenceNumber);
                
                	byte[] attachmentData = null;
                	System.out.print("Do you want to add an attachment? (yes/no): ");
                	if ("yes".equalsIgnoreCase(scanner.nextLine())) {
                		System.out.print("Type the attachment path: ");
                		String attachmentPath = scanner.nextLine();
                		File file = new File(attachmentPath); // File object is created with the provided path
                		if (file.exists() && file.isFile()) { // If valid
                			attachmentData = Files.readAllBytes(file.toPath()); // the file's bytes are read
                			attachmentData = Base64.getEncoder().encode(attachmentData); // Base64 encode the file or attachment
                        	
                			// Sending the attachment if present to server
                			if (attachmentData != null) {
                				DatagramPacket sendAttachment = new DatagramPacket(attachmentData, attachmentData.length, serverAddress, 12346);
                				clientSocket.send(sendAttachment);
                				System.out.println("Attachment sent.");
                			}
                        
                			// Checking that the combined size of the email header and attachment does not exceed 1024 bytes
                			if (emailHeaderData.length + attachmentData.length > 1024) {
                				System.out.println("The attachment is too large. Cannot send this email.");
                				continue;
                			}
                		} else {
                			System.out.println("File does not exist or is not a file.");
                		}
                	}
                
                	saveEmailToFile(sequenceNumber, emailHeader, attachmentData); // Saving including attachment data
                
                	// ACK from server after sending email
                	byte[] receiveEmailACK = new byte[1024];
                	DatagramPacket receiveEmailAckData = new DatagramPacket(receiveEmailACK, receiveEmailACK.length);
                	clientSocket.receive(receiveEmailAckData);
                
                	// Converts the received acknowledgment data into a String
                	String serverConfirmation2 = new String(receiveEmailAckData.getData(), 0, receiveEmailAckData.getLength());
                
                	if (serverConfirmation2.startsWith("ACK_" + sequenceNumber)) {
                		System.out.println("ACK received from the server: " + serverConfirmation2); 
                	}
               
                	// Receive final server confirmation message (250, 501, 550)
                	byte[] finalServerConfirmationData = new byte[1024];
                	DatagramPacket finalServerConfirmationPacket = new DatagramPacket(finalServerConfirmationData, finalServerConfirmationData.length);
                	clientSocket.receive(finalServerConfirmationPacket);
                	String finalServerConfirmation = new String(finalServerConfirmationPacket.getData(), 0, finalServerConfirmationPacket.getLength());
                
                	if (finalServerConfirmation.contains("250")) {
                		String timestamp = finalServerConfirmation.substring(11);
                		System.out.println("Email received successfully at " + timestamp);
                	} 
                	else if (finalServerConfirmation.contains("501")) {
                		System.out.println("501 Error - Headers are invalid.");
                	} 
                	else if (finalServerConfirmation.contains("550")) {
                		System.out.println("550 Error - Email address doesn't exist.");
                	} 
                	else {
                		System.out.println("Unknown response received: " + finalServerConfirmation);
                	}
                
                
                	// Sending ACK to sender after receiving final server confirmation message
                	if (finalServerConfirmation.contains("250") || finalServerConfirmation.contains("501") || finalServerConfirmation.contains("550")) {
                		String ackForFinalConfirmation = "ACK_FOR_CONFIRMATION_" + sequenceNumber;
                		byte[] ackForFinalConfirmationBytes = ackForFinalConfirmation.getBytes();
                		DatagramPacket ackForFinalConfirmationPacket = new DatagramPacket(ackForFinalConfirmationBytes, ackForFinalConfirmationBytes.length, serverAddress, 12346);
                		clientSocket.send(ackForFinalConfirmationPacket);
                		System.out.println("Sent acknowledgment for the final confirmation message to the server.");
                	}
                	sequenceNumber++; // Incrementing sequence number
                	System.out.println();
                	System.out.print("Do you want to send another email? Type anything to continue or 'quit' to quit: ");
                	System.out.println();
                	String nextAction = scanner.nextLine();
                
                	// Terminating connection 
                	if ("quit".equalsIgnoreCase(nextAction)) {
                		// Send "TERMINATE_CONNECTION" to the server to indicate intention to close the connection
                		System.out.println("Sending TERMINATE_CONNECTION to the server...");
                		String terminateConnection = "TERMINATE_CONNECTION";
                		byte[] terminateData = terminateConnection.getBytes();
                		DatagramPacket terminationPacket = new DatagramPacket(terminateData, terminateData.length, serverAddress, 12346);
                		clientSocket.send(terminationPacket);
                		// Wait for the server to send "ACK" in response to "TERMINATE_CONNECTION"
                		byte[] ackTerminateData = new byte[1024]; 
                		DatagramPacket ackTerminatePacket = new DatagramPacket(ackTerminateData, ackTerminateData.length);
                		clientSocket.receive(ackTerminatePacket);
                		String ackTerminateMsg = new String(ackTerminatePacket.getData(), 0, ackTerminatePacket.getLength());
                    
                		if ("ACK".equals(ackTerminateMsg.trim())) {
                			System.out.println("Received ACK from the server. Sending ACK ACK...");
                			// Send "ACK ACK" to the server
                			byte[] ackAckTermData = "ACK ACK".getBytes();
                			DatagramPacket ackAckTermPacket = new DatagramPacket(ackAckTermData, ackAckTermData.length, serverAddress, 12346);
                			clientSocket.send(ackAckTermPacket);
                			System.out.println("Final handshake completed. Disconnecting...");
                		} else {
                    		System.out.println("Unexpected response from the server: " + ackTerminateMsg);
                    	}
                    	break;
                	}
                
                	System.out.println();
                }
            
            //OPTION 3: TERMINATE CONNECTION ----------------------------
			else if ((option_of_client == 3)) {
				// Send "TERMINATE_CONNECTION" to the server to indicate intention to close the
				// connection
				System.out.println("Sending TERMINATE_CONNECTION to the server...");
				String terminateConnection = "TERMINATE_CONNECTION";
				byte[] terminateData = terminateConnection.getBytes();
				DatagramPacket terminationPacket = new DatagramPacket(terminateData, terminateData.length,
						serverAddress, 12346);
				clientSocket.send(terminationPacket);
				// Wait for the server to send "ACK" in response to "TERMINATE_CONNECTION"
				byte[] ackTerminateData = new byte[1024];
				DatagramPacket ackTerminatePacket = new DatagramPacket(ackTerminateData, ackTerminateData.length);
				clientSocket.receive(ackTerminatePacket);
				String ackTerminateMsg = new String(ackTerminatePacket.getData(), 0, ackTerminatePacket.getLength());

				if ("ACK".equals(ackTerminateMsg.trim())) {
					System.out.println("Received ACK from the server. Sending ACK ACK...");
					// Send "ACK ACK" to the server
					byte[] ackAckTermData = "ACK ACK".getBytes();
					DatagramPacket ackAckTermPacket = new DatagramPacket(ackAckTermData, ackAckTermData.length,
							serverAddress, 12346);
					clientSocket.send(ackAckTermPacket);
					System.out.println("Final handshake completed. Disconnecting...");
				} else {
					System.out.println("Unexpected response from the server: " + ackTerminateMsg);
				}
				break;
			}
				
            }
            
            scanner.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (clientSocket != null && !clientSocket.isClosed()) {
                clientSocket.close();
            }
        }
    }
   
    // Getting input from the user where scanner is for the input, string is for the email fields, and suffix is for '@example.com'
	private static String getEmailInput(Scanner scanner, String field, String suffix) {
	    String input;
	    do {
	        System.out.print(field + ": ");
	        input = scanner.nextLine();
	        if (!input.endsWith(suffix)) {
	            System.out.println("Invalid " + field + " address. Must end with " + suffix + ". Please re-enter.");
	        }
	    } while (!input.endsWith(suffix)); // Checks that it has the right email format
	    return input;
	}

    // Checks for if the email fields are empty
	private static String getNonEmptyInput(Scanner scanner, String field) {
	    String input;
	    do {
	        System.out.print(field + ": ");
	        input = scanner.nextLine();
	        if (input.isEmpty()) {
	            System.out.println(field + " cannot be empty. Please enter a " + field.toLowerCase() + ".");
	        }
	    } while (input.isEmpty()); // Asks to reenter if empty
	    return input;
	}
	
	private static void saveEmailToFile(int sequenceNumber, String emailContent, byte[] attachmentData) throws IOException {
	    String directoryPath = "SentEmails"; // The email will be saved to a folder with this name
	    File directory = new File(directoryPath); 
	    if (!directory.exists()) {
	        directory.mkdirs();
	    }
	    // Save email content
	    if (emailContent != null && !emailContent.isEmpty()) {
	        String emailFilePath = directoryPath + "/Email_" + sequenceNumber + ".txt";
	        Files.write(Paths.get(emailFilePath), emailContent.getBytes());
	        System.out.println("Email content saved to " + emailFilePath);  
	    }
	    
	    // Save attachment if present
	    if (attachmentData != null) {
	        String attachmentFilePath = directoryPath + "/Attachment_" + sequenceNumber;
	        Files.write(Paths.get(attachmentFilePath), attachmentData);
	        System.out.println("Email attachment saved to " + attachmentFilePath);
	    }
	}
}



Receiver code


Java
<pre>import java.io.*;
import java.net.*;
import java.util.*;

public class Receiver {
   
	public static void main(String[] args) {
        
    	DatagramSocket receiverSocket = null;
        
        try {
        	
            receiverSocket = new DatagramSocket(12347); // Receiver listens on a specific port
            
            // Initiate connection to the server
            System.out.println("Sending Connect to the server...");
            InetAddress serverAddress = InetAddress.getByName("localhost"); 
            byte[] initData = "Connect".getBytes();
            DatagramPacket initPacket = new DatagramPacket(initData, initData.length, serverAddress, 12346); // Server's port
            receiverSocket.send(initPacket); // Sending connect to the server
            
            // Wait for ACK from the server
            byte[] ackData = new byte[1024];
            DatagramPacket ackPacket = new DatagramPacket(ackData, ackData.length);
            
            // Check if ACK is received from server,
            // if not received, 
            receiverSocket.receive(ackPacket);
            String ackMsg = new String(ackPacket.getData(), 0, ackPacket.getLength());
            
            if (!"ACK".equals(ackMsg.trim())) {
                System.out.println("Failed to connect to the server. Exiting...");
                return;
            }
            else {
            	System.out.println("Received ACK from the server.");
            }
            
            // Send ACK ACK to complete the handshake
            System.out.println("Sending ACK ACK to the server...");
            byte[] ackAckData = "ACK ACK".getBytes();
            DatagramPacket ackAckPacket = new DatagramPacket(ackAckData, ackAckData.length, serverAddress, 12346);
            receiverSocket.send(ackAckPacket);
            System.out.println("Handshake completed. Listening to incoming emails...");
            System.out.println();
            
            // Ensure the "ReceivedEmails" directory exists
            File directory = new File("ReceivedEmails");
            if (!directory.exists()) {
                directory.mkdir(); // Create the directory if it does not exist
            }
            
            while (true) {
            	
                byte[] receiveBuffer = new byte[1024];
                DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
                receiverSocket.receive(receivePacket);
                
                String receivedEmail = new String(receivePacket.getData(), 0, receivePacket.getLength());
                System.out.println("Received email: " + receivedEmail);
                
                // Acknowledgment for the received email sent to the server
                String ackResponse = "ACK_FROM_RECEIVER";
                byte[] ackResponseBytes = ackResponse.getBytes();
                DatagramPacket ackPacket2 = new DatagramPacket(ackResponseBytes, ackResponseBytes.length, receivePacket.getAddress(), receivePacket.getPort());
                receiverSocket.send(ackPacket2);
                System.out.println("Sent acknowledgment ACK for the received email.");
                
                //Saving the email to a file in the "Received Emails" directory
                String fileName = "ReceivedEmails/email_" + System.currentTimeMillis() + ".txt";
                try (FileOutputStream fos = new FileOutputStream(fileName)) {
                    fos.write(receivedEmail.getBytes());
                    System.out.println("Saved received email to file: " + fileName);
                } catch (IOException e) {
                    System.err.println("Error writing the received email to a file: " + e.getMessage());
                }
                
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (receiverSocket != null && !receiverSocket.isClosed()) {
                receiverSocket.close();
            }
        }
    }
}
Posted
Updated 30-Apr-24 8:21am
v2
Comments
Dave Kreskowiak 30-Apr-24 14:04pm    
Help with what? You haven't described any problems with all the code you posted.
Ezma 30-Apr-24 14:18pm    
I have updated the server code with the ClientHandler, however I dont know how to make the multi threaded server so i can allow more than one client to send emails

1 solution

While we are more than willing to help those that are stuck, that doesn't mean that we are here to do it all for you! We can't do all the work, you are either getting paid for this, or it's part of your grades and it wouldn't be at all fair for us to do it all for you.

So we need you to do the work, and we will help you when you get stuck. That doesn't mean we will give you a step by step solution you can hand in!
Start by explaining where you are at the moment, and what the next step in the process is. Then tell us what you have tried to get that next step working, and what happened when you did.

And threading is complicated: you have to think pretty hard before you get started, and your course notes should help you to understand what you have to do - it isn't a case of "call this and it'll work", there are a lot of things you need to consider, like locking, deadlocks, and processor capabilities. Once you have read all your course notes, start here: Java Concurrency and Multithreading Tutorial[^] but bear in mind that the specifics may vary between OSes.

If you are having problems getting started at all, then this may help: How to Write Code to Solve a Problem, A Beginner's Guide[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900