0
\$\begingroup\$

The following code was my first attempt at creating and manipulating socket variables. I compiled and ran the code through cmd.

What are the most common ways to organize methods in both a server and client program?

Client program:

import java.util.Scanner;
import java.io.InputStreamReader;
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.IOException;
import java.net.Socket;


public class Client_Example_Bank {
    public static void main (String[] args) throws IOException {

        // user connect / disconect int
        int conInt = 1;
        int complInt = 1;
        int money = 0;
        int sTalk = 1;


        // Scanner is needed to accept data from the user.
        Scanner scanUI = new Scanner(System.in);

        // a socket which will direct the client to the server using the name or IP address and the port number that the server will also use.
        Socket s = new Socket("DESKTOP-1HPRSDS", 8901);

        // A second scanner will collect data from the server.
        Scanner scanSI = new Scanner(s.getInputStream());

        // Create a printstream object to pass data to the server.
        PrintStream ps = new PrintStream(s.getOutputStream());

        // initialize variables for data sent between client and server
        String clientString = " ";
        String serverString = " ";

        try {

            // main while loop running while the connection is still in progress
            while (conInt == 1){

                // while the server is talking
                while (sTalk == 1){

                    serverString = scanSI.next();

                    // a is the character I chose to let the client know when it is time to respond
                    // if not a the data being sent needs to be displayed on the client side.
                    if (serverString.equalsIgnoreCase("a")){
                        sTalk = 0;
                    }
                    else {
                        System.out.println(serverString);
                    }
                }

                // clear the scanner after all information has been sent and the server has indicated with
                // the character a that it is waiting on input.
                scanSI = new Scanner(s.getInputStream());

                // while the server is not talking and is waiting for the client to give info
                while (sTalk == 0){
                    System.out.println("waiting for user input...");
                    clientString = scanUI.next();
                    ps.println(clientString);
                    sTalk = 1;
                }
            }
        }catch (Exception e){
            System.out.println("Goodbye");
            s.close();
        }
    }
}

Server Program:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.IOException;
import java.net.Socket;
import java.net.ServerSocket;
import java.util.Date;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.io.PrintStream;

public class Server_Example_Bank {


    public static void main (String[] args) throws IOException {

        // variables for while loops
        int conInt = 1;
        int clientTalk = 0;
        int serverTalk = 0;
        int step = 0;
        int money = 0;
        int moneyChange = 0;

        // create a server socket so that clients can connect through the servers specified port
        ServerSocket ss = new ServerSocket(8901);

        // this will accept incoming connections
        Socket cs = ss.accept();

        // collects data that a client is trying to pass to server
        Scanner sc = new Scanner (cs.getInputStream());

        // declare variable to hold client data
        String clientString = " ";

        // A variable to send data back to the client
        PrintStream p = new PrintStream(cs.getOutputStream());

        // listening and talking method for client/server with bank processes
        do {

            // while client is not talking or when app is started
            while (clientTalk == 0){
                mainMenu(p);
                clientTalk = 1;
            }

            // while client is talking take user input and put it into a string variable
            while (clientTalk == 1){
                System.out.println("Waiting.for.user.input...");
                clientString = sc.next();

                System.out.println("user.input.=."+clientString);

                clientTalk = 2;
            }

            // if user input is b (stands for balance)
            if (clientString.equalsIgnoreCase("b")){
                p.println(" ");
                p.println("Your.Balance.Is: "+money);
                p.println(" ");
            }
            // if user input is d (stands for deposit)
            if (clientString.equalsIgnoreCase("d")){
                p.println(" ");
                p.println("Deposit.ammount:");
                p.println("a");
                clientString = sc.next();
                // parseInt from the string of user input to make sure the value is an integer
                moneyChange = Integer.parseInt(clientString);
                money = money + moneyChange;
                clientString = "";
                p.println("Your.new.total.is: "+money);
                p.println(" ");

            }
            // if user input is w (stands for withdraw)
            if (clientString.equalsIgnoreCase("w")){
                p.println(" ");
                p.println("Enter.Withdraw.Ammount");
                p.println("a");
                clientString = sc.next();
                // parseInt from the string of user input to make sure the value is an integer
                moneyChange = Integer.parseInt(clientString);
                // if there is not enough in the account to make the withdraw
                if ((money - moneyChange)< 0){
                    p.println("Not.Enough.Funds");
                }
                else {
                    money = money - moneyChange;
                    p.println("Your.new.total.is: "+money);
                }
                p.println(" ");

            }
            // if user has completed their transaction but is on the main menu
            if (clientString.equalsIgnoreCase("t")){
                conInt = 1;
            }

            // clear client string
            clientString = "";

            // this starts the second conversation between the client and server to find out if the
            // user wants to complete another transaction in their account now that one has been completed
            while (clientTalk == 2){

                p.println(" ");
                p.println("Did.you.want.to.make.another.transaction?.y.or.n");
                p.println("a");
                clientString = sc.next();
                System.out.println("User entered "+clientString);

                if (clientString.equalsIgnoreCase("n")){
                    closeConnection(cs);
                }
                else if (clientString.equalsIgnoreCase("y")){
                    conInt = 0;
                    clientTalk = 0;
                }
                else {
                    p.println("Invalid Entry");
                }
                p.println(" ");
            }

            // clear client string
            clientString = "";
        }while(conInt == 0);

        System.out.println("Disconnecting from client");
        closeConnection(cs);
    }

    // close method
    public static void closeConnection(Socket skt){
        try {
            skt.close();
        }catch(Exception e){
            System.out.println("Client Disconnected");
        }
    }

    // main menu method
    public static void mainMenu(PrintStream pS){
        pS.println(" ");
        pS.println(" ");
        pS.println("Welcome.to.your.bank.account.");
        pS.println("What.would.you.like.to.do.today?");
        pS.println(" ");
        pS.println("View.Balance: type.b");
        pS.println("Deposit.Funds: type.d");
        pS.println("Withdraw.Funds: type.w");
        pS.println("Transaction.Complete: type.t");
        pS.println(" ");
        pS.println("a");
    }
}
\$\endgroup\$
1
\$\begingroup\$

You appear to be new to Java. For one thing, you are using integers to store ones and zeros to be used as flags. Java has a boolean type to handle that. If you need to store more than true or false, then you can use an Enum to signify the separate "states" you wish to convey.

You might consider using separate threads for reading and writing to and from the server. However, your current design does not really need this, since it's a simple back and forth of messages in pre-ordained sequence. More complicated scenarios could be handled by having separate threads, one reading from the user, another sending messages to server, a third reading messages from server.

Your while (sTalk == 0) loop should not be a loop. In fact, it is a common pattern in your code for you to put in loops that never actually loop. Most of your "while" loops do not need to be there, none of them will ever repeat the loop. They are really just "if" statements.

You use a the scanner class in this case, a more robust solution would use a messaging library like protobuf, or JSON, or http, or use some sort of protocol to organize data being sent into messages. Those three I listed are popular, but the basic idea is that you define what a "message" looks like and you always send data as a "message". That way you would not be making assumptions about what is being sent over the socket (ie you magically know when to expect a string or an int or whatever). A message would have a format, perhaps a header, a length, and you would read each message all together as a unit. You would also not need the magic" character 'a' to have a special meaning. Having a protocol defined allows you to avoid making such assumptions. JSON is a message type that does not even require a header, you might look into that one, it is a very simple protocol and very popular these days.

You should probably not catch "Exception", catch "IOException" or whatever is specifically thrown, and you should have try/catch around the different places you do I/O to be able to differentiate the different issues that occur.

Overall, I would say that your code is long-winded and not broken up enough into individual pieces. It also makes too many assumptions about the ordering regarding who says what and when.

A simple client design would simply be like this:

while (true) {
    read from client a command
    based on what the command is, compose the command into a message for the server
    send the full message to server as a sequence of bytes to the socket
    read a single response message from server, read all the bytes for a single message
    output the result to user
}

server:

while(true) {
    read all the bytes for a single message from client
    do whatever based on that message
    send a single response message to client
}

You would not have all those extra while loops all over your code. Each message would be a header indicating the number of bytes in the message and the content, or some other protocol.

Each one of those steps would be a separate function. So your main loop would be about 10 lines of code (like my pseudo-code), and would be easy to read and understand.

\$\endgroup\$
  • \$\begingroup\$ Thank you this is all very helpful advice. I will implement it and update the code here soon. \$\endgroup\$ – Allie Marie Apr 3 '18 at 6:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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