Search code examples
javamultithreadingswingsocketsserver

Java Socket server accepts only one request and stops receiving data


I am making a Client-Server Java Swing application where I need to send requests through ObjectOutputStream. I make one request from LoginFrame to check if login/password is correct, server receives data, makes manipulations with it and successfully sends answer back. But when I try to make another call to server, it just doesnt receive it, server kind of stops listening.

Server:

public void runServer() throws IOException, ClassNotFoundException {

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Server started and listening " + port);
        Model.getCountriesAndCities();
        System.out.println(Model.getCountries()[0]);
        System.out.println(Model.getCountriesMap());

        for (int i = 0; i < 50; i++) {
            Socket clientSocket = serverSocket.accept();
            System.out.println("serversocket accepted");
            System.out.println("before oos");
            ObjectOutputStream oos = new ObjectOutputStream(clientSocket.getOutputStream());
            System.out.println("before ois");
            ObjectInputStream ois = new ObjectInputStream(clientSocket.getInputStream());
            System.out.println("server is listening");

            Map<String, Object> methodData = (Map<String, Object>) ois.readObject();
            System.out.println("Server: data received");

            String methodName = (String) methodData.get("method");
            Object arguments = methodData.get("arguments");

            Object result = invokeMethod(methodName, arguments);

            oos.writeObject(result);
            System.out.println("Server: data sent");
            oos.flush();
        }
    }

Client methods:

public static int checkUser(String login, String password, Client client) {
        String query = "SELECT EXISTS(SELECT * FROM users WHERE login = '" + login + "' and password = '" + password + "') as isExist";
        String methodName = "CRUDUtils.checkUser";
        try {
            Map<String, Object> methodData = new HashMap<>();
            methodData.put("method", methodName);
            methodData.put("arguments", query);

            client1.oos.writeObject(methodData);
            System.out.println("Client: query sent");
            client1.oos.flush();

            int check = (Integer) client1.ois.readObject();
            System.out.println("chekUser: answer received");
            return check;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
public static String[] getCountries(Client client) {
        String query = " ";
        String methodName = "Model.getCountries";
        ObjectOutputStream oos;
        ObjectInputStream ois;
        try {
            Map<String, Object> methodData = new HashMap<>();
            methodData.put("method", methodName);
            methodData.put("arguments", query);

            client1.oos.writeObject(methodData);
            System.out.println("Client: query sent");
            client1.oos.flush();

            System.out.println("before ois");
            String[] check = (String[]) client1.ois.readObject();
            System.out.println("getCountries: answer received");
            return check;
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

I also have Client class to start socket and swing Frame:

public static void main(String[] args) {
        Client klientura = new Client();
        try {
            klientura.socket = new Socket("localhost", 4444);
            klientura.oos = new ObjectOutputStream(klientura.socket.getOutputStream());
            klientura.ois = new ObjectInputStream(klientura.socket.getInputStream());
            System.out.println("client connected");
            Control.client1 = klientura;
            LoginFrame loginFrame = new LoginFrame(klientura);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

I tried to replace ObjectOutput and InputOutput, it didn't work. I tried to flush every oos after sending data, it didn't work. I tried to close every oos and ois after using them, it didn't work. I tried everything, thats why this code right now is a mess.

Method checkUser works just fine, but when it comes to run getCountries, the client process just stops on line String[] check = (String[]) client1.ois.readObject(); The last message in client console is "before ois", it means that client1.oos.writeObject(methodData) is completed, but server doesnt respond to that. The last message in server console is "Server: data sent", it means that server sent data to checkUser method and stopped listening.


Solution

  • As per the documentation, accept() listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.

    Your current implementation looks like it is re-using the same connection for each request. Therefore, the server is blocking at the accept() method because the client is never making a new connection.

    I would suggest leveraging threading on the server. On the main thread listen for new connections. Once a connection is made, the accept() method will return a Socket instance. At this point create a new thread and pass in the socket reference. Have all of the request/response processing logic in the new thread.

    This will allow you to read and write to sockets, while also listening for new connections to the server.

    public void runServer() {
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Server started and listening " + port);
    
        while(true){
            try {
                Socket socket = serverSocket.accept();
                Task task = new Task(socket);
                (new Thread(task)).start();
            } catch(IOException e){
                // Handle 
            }
        }
    }
    
    public class Task implements Runnable {
    
        private final Socket socket;
    
        public Task(Socket socket) {
            this.socket = socket;
        }
    
        @Override
        public void run() {
            // Loop that reads from and writes to socket
        }
    }