Многопоточный сокет клиент / сервер Java: отправка и получение объектов Enummap

Начнем с того, что это JavaFX приложение с классической архитектурой MVC. Это приложение бросает кубики и отображает их. Цель состоит в том, чтобы показать их нескольким клиентам. Я использую многопоточный "эхо" сервер с сокетами для работы с клиентами. Поскольку в JavaFX мы не можем напрямую отправлять узлы через сокет, я решил отправить аргументы, сгенерированные клиентом, а затем клиент отправляет их на сервер, чтобы он мог отобразить аргументы всем подключенным клиентам.

Вот как это работает:

Сначала создается главный серверный поток. Это создает ServerSocket и цикл while создает новый поток, который будет обрабатывать подключенный клиент. Этот основной поток сервера имеет 3 метода: 2, который будет отслеживать подключенного клиента, и 1, который отправляет входящие аргументы всем подключенным клиентам.

public class DiceRollServerThread implements Runnable
{
    private Server _server;
    private Server_C controller;
    private Vector<ObjectOutputStream> tabClients = new Vector<ObjectOutputStream>(); // Contain all outpput streams to connected clients
    private Thread t;
    private ServerSocket diceSS;

    public DiceRollServerThread(Server server) throws IOException
    {
        _server = server;

        controller = _server.getController();

        String port = "2000";
        String ip = "127.0.0.1";

        controller.setConsole("IP : "+ip+"\n"+"Port : "+port);

        diceSS = new ServerSocket(Integer.parseInt(port), 0, InetAddress.getByName(null));

        t = new Thread(this);
        t.start();
    }

    @Override
    public void run()
    {
        while (true) // bloquing on ss.accept
        {
            try
            {
                new DiceRollThread(diceSS.accept(), this);
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    synchronized public void sendAll(EnumMap<ARGS, String> arguments) throws IOException
    {
        ObjectOutputStream out;

        for (int i = 0; i < tabClients.size(); i++) // browsing connected clients
        {
            out = (ObjectOutputStream) tabClients.elementAt(i);
            if (out != null)
            {
                out.writeObject(arguments);
                out.flush();
            }
        }
    }

    synchronized public void delClient(int i)
    {
        if (tabClients.elementAt(i) != null) // If element exist ...
        {
            tabClients.removeElementAt(i); // ... delete it
            System.out.println("delClient");
        }
    }

    synchronized public int addClient(ObjectOutputStream out)
    {
        tabClients.addElement(out); // Adding new output stream to vector
        System.out.println("addClient");    
        return tabClients.size()-1; // return client number (size-1)
    }

    public Server get_server()
    {
        return _server;
    }
}

Вот поток обработки клиентов:

public class DiceRollThread implements Runnable
{
    private Thread t;
    private Socket _s;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private DiceRollServerThread _serverThread; // to use main thread methods
    private int numClient=0;
    private Server _server;
    private EnumMap<ARGS,String> _arguments;

    DiceRollThread(Socket s, DiceRollServerThread serverThread)
    {
        _s = s;
        _serverThread = serverThread;
        _server = _serverThread.get_server();

        try
        {
            out = new ObjectOutputStream(_s.getOutputStream());
            in = new ObjectInputStream(_s.getInputStream());
            numClient = _serverThread.addClient(out);
            _server.getController().setConsole("Client n°"+numClient+" connected.");
        }
        catch (IOException e)
        {

        }

        t = new Thread(this);
        t.start();
    }

    @Override
    public void run()
    {
        try
        {
            while(_s.getInputStream().read() != -1) // verifying if connection is still up
            {
                _arguments = (EnumMap<ARGS, String>) in.readObject(); // Problem is here

                if(_arguments != null)
                {
                    System.out.println(_arguments);
                    _serverThread.sendAll(_arguments);
                }
            }
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally // Usually happens when client disconnect
        {
            try
            {
                _server.getController().setConsole("Client n°"+numClient+" disconnected.");
                _serverThread.delClient(numClient); // deleting from the vector
                _s.close(); // Closing socket if not done by upper exception
            }
            catch (IOException e)
            {

            }
        }
    }

Теперь на стороне клиента. Основное приложение имеет контроллер, который получит значение двух полей и сохранит их в следующем Enummap

public class Arguments
{
    public enum ARGS {Couleur,Valeur};
}

А затем отправляет его следующему потоку, обрабатывающему соединение с сервером.

private void roll()
{
    arguments.put(ARGS.Couleur, box_couleur.getValue().toString());
    arguments.put(ARGS.Valeur, randInt(1,Integer.parseInt(box_de.getValue())));

    diceRollThread.send(arguments); // the thread gives his reference to the controller when created
}

Клиентский поток (который подключен к серверу)

public class DiceRollThread implements Runnable
{
    private DiceRoll_C _controller;
    private Socket s;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private Thread t;
    private EnumMap<ARGS,String> _arguments;

    DiceRollThread(DiceRoll diceroll) throws IOException
    {   
        _controller = diceroll.getController();
        _controller.setDiceRollThread(this);

        String port = "2000";
        String ip = "127.0.0.1";

        try
        {
            s = new Socket(InetAddress.getByName(ip),Integer.parseInt(port));
            out = new ObjectOutputStream(s.getOutputStream());
            in = new ObjectInputStream(s.getInputStream());
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        t = new Thread();
        t.start();
    }

    @Override
    public void run()
    {
        try
        {
            _arguments = (EnumMap<ARGS, String>) in.readObject();

            if(_arguments != null)
            {
                _controller.addDice(_arguments); // Creating dice from received arguments
            }
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                s.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    public void send(EnumMap<ARGS,String> arguments) // arguments received from the controller, sends them to the server
    {
        try
        {
            out.writeObject(arguments);
            out.flush();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

Типичный сценарий выглядит следующим образом: запуск сервера> запуск клиента (соединение успешно установлено) > нажатие кнопки пользователя. Когда пользователь нажимает на кнопку, 2 значения сохраняются в enummap, созданном контроллером, контроллер перенаправляет это enummap в поток клиента с помощью метода send(); и этот метод записывает enummap в поток вывода объекта.

Проблема происходит на следующем этапе. Поток, обрабатывающий клиентское соединение на стороне сервера, получает enummap из потока

_arguments = (EnumMap<ARGS, String>) in.readObject();

Но похоже, что он не может привести его к Enummap и выдает исключение

java.lang.ClassCastException: java.io.ObjectStreamClass не может быть приведен к java.util.EnumMap

на java.lang.Thread.run(неизвестный источник)

Что я делаю неправильно?

1 ответ

Решение
while(_s.getInputStream().read() != -1) // verifying if connection is still up

Проблема здесь. Он потребляет байт из входного потока и, следовательно, выводит сериализацию из синхронизации с отправителем.

Это также не достигает цели, указанной в комментарии. Правильный способ сделать это просто поймать IOException: connection reset это будет результатом отправки на разорванное соединение, или EOFException это результат чтения конца потока.

Другие вопросы по тегам