0

I am trying to create a simple MVC client-server using a calculator app. When I try calculate/return the value my GUI freezes. I am not sure exactly what the problem is but I suspect it has something to do with my while(true) in my ControllerClient. I am unsure if I am communicating with the sockets correctly. I am using strings to communicate between client and server

package Client.Controller;

import java.awt.event.ActionEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import Client.View.CalculatorView;

public class ClientController {
    private CalculatorView view;
    private Socket clientSocket;
    private PrintWriter socketOut;
    private BufferedReader socketIn;
    
    
    public ClientController(CalculatorView view, String serverName, int portNum) throws IOException {
        this.view = view;
        clientSocket = new Socket(serverName, portNum);
        socketIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        socketOut = new PrintWriter((clientSocket.getOutputStream()), true);
    }
    
    public void calculate() {
        
        String firstNum = Integer.toString(view.getFirstNumber());
        String secNum = Integer.toString(view.getSecondNumber());
        
        socketOut.println("1" + firstNum + "," + secNum);
        view.setTheSolution(Integer.parseInt(readFromServer()));
        
    }
    
    public void communicate() {
        view.setVisible(true);
        view.addActionListener((ActionEvent e)->  {
            calculate();
        });
    }
    
    private String readFromServer() {
        String s = "";
        while(true) {
            try {
                s += socketIn.readLine() + "\n";
                if(s.contains("\0")) {
                    s = s.replace("\0", "\n");
                    return s;
                }
                
            } catch (IOException e) {
                System.out.println("Error in reading from server.");
                e.printStackTrace();
            }
            System.out.println(s);
        }
    }
    
    
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        CalculatorView view = new CalculatorView();
        try {
            ClientController control = new ClientController(view, "localhost", 9090);
            control.communicate();
        }
        catch(IOException e) {
            System.out.println("Error in main client controller");
            e.printStackTrace();
        }

    }

}

View:

package Client.View;
import java.awt.event.ActionListener;

import javax.swing.*;

public class CalculatorView extends JFrame {
    
    private JTextField firstNumber = new JTextField(10);
    private JLabel addLabel = new JLabel("+");
    
    private JTextField secondNumber = new JTextField(10);
    
    private JButton calculateButton = new JButton("Calculate");
    
    private JTextField theSolution = new JTextField(10);
    
    public CalculatorView() {
        
        JPanel calcPanel = new JPanel();
        setSize(300, 300);
        
        calcPanel.add(firstNumber);
        calcPanel.add(addLabel);
        calcPanel.add(secondNumber);
        calcPanel.add(calculateButton);
        calcPanel.add(theSolution);
        
        add(calcPanel);
    }
    
    public int getFirstNumber() {
        return Integer.parseInt(firstNumber.getText());
    }
    public int getSecondNumber() {
        return Integer.parseInt(secondNumber.getText());
    }   
    public void setTheSolution(int solution) {
        this.theSolution.setText(Integer.toString(solution));
    }
    public void addActionListener(ActionListener listenForCalculateButton) {
        calculateButton.addActionListener(listenForCalculateButton);
    }

}

server model app

package Server.Model;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;

public class Application implements Runnable{
    
    private CalculatorModel calc;
    PrintWriter socketOut;
    BufferedReader socketIn;
    
    public Application(CalculatorModel calc, PrintWriter socketOut, BufferedReader socketIn) {
        this.calc = calc;
        this.socketOut = socketOut;
        this.socketIn = socketIn;
    }
    
    public void menu() throws IOException {
        int input;
        int firstNum;
        int secNum;
        
        while(true) {
            String request = socketIn.readLine();
            String[] args = request.split(",");
            input = Integer.parseInt(args[0]);
            switch(input) {
            case 1:
                firstNum = Integer.parseInt(args[1]);
                secNum = Integer.parseInt(args[2]);
                addNum(firstNum, secNum);
                break;
                
            default:
                System.out.println("Quitting Program");
                return;
            }
        }
    }
    
    public synchronized void addNum(int firstNum, int secNum) {
        String s ="";
        calc = new CalculatorModel();
        calc.addTwoNumbers(firstNum, secNum);
        s = Integer.toString(calc.getResult());
        socketOut.println(s + "\0");
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            menu();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

}

the model

package Server.Model;

public class CalculatorModel {

    private int result;
    
    public void addTwoNumbers(int a, int b) {
        result = (a + b);
    }

    public int getResult() {
        return result;
    }

}

my server controller

package Server.Controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import Server.Model.Application;
import Server.Model.CalculatorModel;

public class ServerController {
    ServerSocket serverSocket;
    ExecutorService pool;
    CalculatorModel calc;
    
    public ServerController(int portNum, CalculatorModel calc) {
        try {
            serverSocket = new ServerSocket(portNum);
            pool = Executors.newCachedThreadPool();
            this.calc = calc;
            System.out.println("Server is running...");
        } catch (IOException e) {
            System.out.println("Error in creating server controller.");
            e.printStackTrace();
        }
    }
    
    public void communicate() {
        while(true) {
            try {
                Socket socket = serverSocket.accept();
                BufferedReader socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                PrintWriter socketOut = new PrintWriter(socket.getOutputStream(), true);
                
                String request = socketIn.readLine();
                String[] args = request.split(",");
                int firstNum = Integer.parseInt(args[0]);
                int secNum = Integer.parseInt(args[1]);
                
                Application app = new Application(calc, socketOut, socketIn);
                pool.execute(app);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        CalculatorModel calc = new CalculatorModel();
        ServerController server = new ServerController(9090, calc);
        server.communicate();
    }

}
Morello
  • 258
  • 2
  • 10
  • 1
    Are you running your network calls on the GUI thread? (I cannot follow your code clearly.) – Basil Bourque Nov 03 '21 at 21:49
  • yes, they are ran in the clientcontroller class. I then use a switch in the server model app class to receive the requests – Morello Nov 03 '21 at 21:55
  • 1
    All long-running code must be run in its own thread, and the GUI updated by queuing information to the GUI thread as per the [Concurrency in Swing](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/) link. – Hovercraft Full Of Eels Nov 03 '21 at 22:13
  • 1
    There is no [mre] posted with your question, and so we are not able to directly debug the issue to find the exact cause. Still, this is undoubtedly a concurrency issue. – Hovercraft Full Of Eels Nov 03 '21 at 22:15
  • sorry I will add the remaining code – Morello Nov 03 '21 at 22:16
  • 2
    My assumption has been proved correct. `view.setTheSolution(Integer.parseInt(readFromServer()));` -- here is where you break Swing threading rules. Again, long-running code, such as reading in from the server, *must* be done on a background thread, such as a SwingWorker, and then queued to the GUI via the EDT, the event thread. – Hovercraft Full Of Eels Nov 03 '21 at 22:23
  • https://en.wikipedia.org/wiki/SwingWorker and [*Lesson: Concurrency in Swing*](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) – Basil Bourque Nov 03 '21 at 22:44
  • Always [search](https://stackoverflow.com/search?q=java+swing+freezes) thoroughly before posting. Duplicate of [this](https://stackoverflow.com/q/9419252/642706) and many others. – Basil Bourque Nov 03 '21 at 22:45
  • Thank you for your feedback but I am still confused as to where I would set this in my code – Morello Nov 03 '21 at 22:49
  • You may not be at the point to do this yet. Read the [concurrency in Swing](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/) link first and *fully*, and then try to change your code to use a SwingWorker. – Hovercraft Full Of Eels Nov 03 '21 at 23:56

0 Answers0