3
\$\begingroup\$

I've been assigned a tast to implement an SMTP client-server protocol providing limited functionality and the Solution for this is to execute a typical execution simiilar to this transcription:

S: 220 [ip]
C: HELLO [email]
S: 250 Hello [email], pleased to meet you
C: MAIL FROM: <[email protected]>
S: 250 ok
C: RCPT TO: <[email protected]>
S: 250 ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: Hello,
C: Test Email
C: .
S: 250 ok Message accepted for delivery
C: QUIT
S: 221 [ip] closing connection

Essentially, it's not supposed to send any emails or anything but more simulate the process of sending an email through an SMTP.

I've taken the easy route of using Cases and Switches so when it reads the data, it prints out exactly what i want, but i want to know if there's a way i can get the Client's input and read it in the server to produce an ouptut similar to the trasncript i've written. And also how can i code it any better?

Here's the Server code:

 package TCPSocket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer{
    private ServerSocket server;

    /**
     * The TCPServer constructor initiate the socket
     * @param ipAddress
     * @param port
     * @throws Exception
     */
    public TCPServer(String ipAddress, int port) throws Exception {
        if (ipAddress != null && !ipAddress.isEmpty())
            this.server = new ServerSocket(port, 1, InetAddress.getByName(ipAddress));
        else
            this.server = new ServerSocket(0, 1, InetAddress.getLocalHost());
    }

    /**
     * The listen method listen to incoming client's datagrams and requests
     * @throws Exception
     */



    private void listen() throws Exception {
        // listen to incoming client's requests via the ServerSocket
        //add your code here
        String data = null;
        Socket client = this.server.accept();
        String clientAddress = client.getInetAddress().getHostAddress();
        System.out.println("\r\nNew client connection from " + clientAddress);

        // print received datagrams from client
        //add your code here
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        while ( (data = in.readLine()) != null ) {
            //System.out.println("\r\nMessage from " + clientAddress + ": " + data);

            switch (data){
                case("HELLO 192.168.56.1"):
                    System.out.println("\r\n250 Hello " + clientAddress + ", pleased to meet you");
                    break;
                case("MAIL FROM: <[email protected]>"):
                    System.out.println("\r\n250 ok");
                    break;
                case("RCPT TO: <[email protected]>"):
                    System.out.println("\r\n250 ok");
                    break;
                case("DATA"):
                    System.out.println("\r\n354 End data with <CR><LF>.<CR><LF>");
                    break;
                case("."):
                    System.out.println("\r\nok Message accepted for delivery");
                    break;
                case("QUIT"):
                    System.out.println("\r\n221 " + clientAddress + " closing connection");
                    in.close();
                    break;
            }

            client.sendUrgentData(1);
        }
    }

    public InetAddress getSocketAddress() {
        return this.server.getInetAddress();
    }

    public int getPort() {
        return this.server.getLocalPort();
    }


    public static void main(String[] args) throws Exception {
        // set the server address (IP) and port number
        //add your code here
        String serverIP = "192.168.56.1"; // local IP address
        int port = 7077;

        if (args.length > 0) {
            serverIP = args[0];
            port = Integer.parseInt(args[1]);
        }
        // call the constructor and pass the IP and port
        //add your code here
        TCPServer server = new TCPServer(serverIP, port);
        System.out.println("\r\nRunning Server: " +
                "Host=" + server.getSocketAddress().getHostAddress() +
                " Port=" + server.getPort());
        System.out.println("220 " + serverIP);
        server.listen();
    }

}

And here's the Client code:

package TCPSocket;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class TCPClient{
    private Socket tcpSocket;
    private InetAddress serverAddress;
    private int serverPort;
    private Scanner scanner;


    /**
     * @param serverAddress
     * @param serverPort
     * @throws Exception
     */
    private TCPClient(InetAddress serverAddress, int serverPort) throws Exception {
        this.serverAddress = serverAddress;
        this.serverPort = serverPort;

        //Initiate the connection with the server using Socket.
        //For this, creates a stream socket and connects it to the specified port number at the specified IP address.
        //add your code here
        this.tcpSocket = new Socket(this.serverAddress, this.serverPort);
        this.scanner = new Scanner(System.in);
    }

    /**
     * The start method connect to the server and datagrams
     * @throws IOException
     */
    private void start() throws IOException {
        String input;

        //create a new PrintWriter from an existing OutputStream (i.e., tcpSocket).
        //This convenience constructor creates the necessary intermediateOutputStreamWriter, which will convert characters into bytes using the default character encoding
        //You may add your code in a loop so that client can keep send datagrams to server
        //add your code here

        while (true) {
            input = scanner.nextLine();
            PrintWriter output = new PrintWriter(this.tcpSocket.getOutputStream(), true);
            //output.println("hello " + serverAddress);
            output.println(input);
            output.flush();
        }
    }

    public static void main(String[] args) throws Exception {
        // set the server address (IP) and port number
        //add your code here
        InetAddress serverIP = InetAddress.getByName("192.168.56.1"); // local IP address
        int port = 7077;

        if (args.length > 0) {
            serverIP = InetAddress.getByName(args[0]);
            port = Integer.parseInt(args[1]);
        }

        // call the constructor and pass the IP and port
        //add your code here
        TCPClient client = new TCPClient(serverIP, port);
        System.out.println("\r\n Connected to Server: " + client.tcpSocket.getInetAddress());
        client.start();
    }
}

Hopefully what i've written made sense and i appreciate any feedback and help provided

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

This is a side issue really, but if your client and server are running on the same machine you may want to consider using the loop back address "127.0.0.1" when connecting from the client, or having it as a command line parameter. It'll mean that you don't need to recompile the application every time you move machines / your DNS decides to reallocate your address.

Responsibilities

Aim to have your classes responsible for one main thing. Typically, I would expect the job of a TCPServer to be to listen on a given port for connection requests, which are then accepted and passed onto another class (possibly something like EmailClient) to manage the connection. The server can then go back to waiting for the next connection request. The way you've coded it up at the moment, the server can't start dealing with the next request until after it's received all of the messages from the first connection. This means that a faulty/malicious client could block your server by simply never closing its connection.

Commands

There are several commands that your server is able to handle. Some of them (QUIT, .) consist of the entire message. Other commands start with a particular message (HELLO, MAIL FROM:), which then have parameters consisting of the rest of the line. Splitting the message between the command and the parameters would allow you to capture relevant information, such as the email address to echo back to the client.

Data

At the moment, although you recognise that you've received a DATA command, you don't do anything about it. Similarly, you always respond to ., as if you'd been processing DATA. Consider using some kind of state in your client class to track what the client is currently doing. If they send DATA, then you start building up the message to send (or ignoring it if you don't care at present), until they send an end message .. While you're processing the message data you don't want to be checking for commands other than end of data. At the moment, if the message contained "MAIL FROM: [email protected]", you'd respond as if it was a command.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.