Проблема связи клиента и сервера через сокет

156
17 сентября 2019, 22:10

Даже не знаю как корректно описать проблему. Вообщем сделал класс Commander, который использует и сервер, и клиент. Его роль в том, чтобы абстрагировать методы отправки и получения данных клиента/сервера. Создалась следующая проблема: клиент вызывает несколько методов этого класса, все работает корректно, но при повторном вызове клиент зависает. Понимаю, что решение кроется в правильном создании потоков ввода вывода, но для начала хочу хотя бы понять в чем же дело, прежде чем начать править.

import java.io.*;
import java.net.Socket;
public class Commander {
    private Socket socket;
    public Commander(Socket socket) {
        this.socket = socket;
    }
    private void sendObject(Serializable object) throws DisconnectException {
        try {
            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
            out.writeObject(object);
            out.flush();
        } catch (IOException e) {
            throw new DisconnectException();
        }
    }
    private Object receiveObject() throws DisconnectException {
        Object object = null;
        try {
            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
            object = in.readObject();
        } catch (IOException e) {
            throw new DisconnectException();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return object;
    }
    public void sendCommand(ServerCommand command) throws DisconnectException {
        sendObject(command);
    }
    public ServerCommand receiveCommand() throws DisconnectException {
        return (ServerCommand) receiveObject();
    }
    public void sendAccount(Account account) throws DisconnectException {
        sendObject(account);
    }
    public Account receiveAccount() throws DisconnectException {
        return (Account) receiveObject();
    }
    public void sendAccountID(int id) throws DisconnectException {
        try {
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
            out.writeInt(id);
            out.flush();
        } catch (IOException e) {
            throw new DisconnectException();
        }
    }
    public int receiveAccountID() throws DisconnectException {
        try {
            DataInputStream in = new DataInputStream(socket.getInputStream());
            int id = in.readInt();
            return id;
        } catch (IOException e) {
            throw new DisconnectException();
        }
    }
    public void sendAnswer(boolean answer) throws DisconnectException {
        try {
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
            out.writeBoolean(answer);
            out.flush();
        } catch (IOException e) {
            throw new DisconnectException();
        }
    }
    public boolean receiveAnswer() throws DisconnectException {
        try {
            DataInputStream in = new DataInputStream(socket.getInputStream());
            boolean answer = in.readBoolean();
            return answer;
        } catch (IOException e) {
            throw new DisconnectException();
        }
    }
    public void closeSocket() {
        try {
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Пробовал вынести все потоки ввода/вывода в private поля, и в конструкторе присваивать им необходимые ссылки, но стало еще хуже.

До этого также была проблема: программа выполняла одну команду и сокет закрывался, после чего вылетала ошибка. Решилось, когда прочитал, что при открытии потока через try-with-resource (а на тот момент так и было), сокет закрывается вместе с потоком.

*Правка: Добавил код серверной части. Этот обработчик запускается через ExecutorService, при новом подключении.

import java.net.Socket;
public class ClientHandler implements Runnable {
    private Socket socket;
    private Commander commander;
    public ClientHandler(Socket socket) {
        this.socket = socket;
        this.commander = new Commander(socket);
    }
    @Override
    public void run() {
        try {
            ServerCommand command = commander.receiveCommand();
            switch (command) {
                case LOGIN:
                    login();
                    break;
                case CREATE_ACCOUNT:
                    createAccount();
                    break;
            }
        } catch (DisconnectException e) {
            System.out.println(socket.getRemoteSocketAddress().toString() + " was disconnected.");
            commander.closeSocket();
        }
    }
    private void login() throws DisconnectException {
        int id = commander.receiveAccountID();
        boolean answer = isAccountExist(id);
        commander.sendAnswer(answer);
        if (answer) {
            commander.sendAccount(Server.getAccounts().get(id));
        }
    }
    private void createAccount() throws DisconnectException {
        Account account = commander.receiveAccount();
        int id = account.hashCode();
        boolean answer = !isAccountExist(id);
        commander.sendAnswer(answer);
        if (answer) {
            Server.getAccounts().put(id, account);
        }
    }
    private boolean isAccountExist(int id) {
        return Server.getAccounts().keySet().contains(id);
    }
}
Answer 1

Вообщем, решение оказалось элементарным. Я банально забыл зациклить принятие команд сервером в методе

    @Override
    public void run() {
        try {
            while (true) {
                ServerCommand command = commander.receiveCommand();
                switch (command) {
                    case LOGIN:
                        login();
                        break;
                    case CREATE_ACCOUNT:
                        createAccount();
                        break;
                }
            }
        } catch (DisconnectException e) {
            System.out.println(socket.getRemoteSocketAddress().toString() + " was disconnected.");
            commander.closeSocket();
        }
    }
READ ALSO
Java класс для Bluetooth Android

Java класс для Bluetooth Android

Всем приветВот пишу отдельный класс java, который будет отвечать чисто только за управление Bluetooth на android-устройстве

135
Корректны ли в Java записи подобного вида?

Корректны ли в Java записи подобного вида?

Приложение работает, но принято ли так писать в Java? Просто для каждой переменной неудобно писать "public static String

143
setPreferredSize Java [закрыт]

setPreferredSize Java [закрыт]

Извините за глупый вопрос, но обязательно ли в качестве аргумента setPreferredSize использовать объект Dimension?

130
Telegram API - InlineKeyboardMarkup - не могу найти библиотеку (JDK11)

Telegram API - InlineKeyboardMarkup - не могу найти библиотеку (JDK11)

Не могу найти класс InlineKeyboardMarkup в библиотеке TELEGRAM API telegrambots-41-jar-with-dependencies

157