Как решить это исключение indexOutOfBoundsException в потоке отправки/получения моего сервера?

Я создаю многопользовательскую игру на Java с сервером и несколькими клиентами. Все работает отлично, пока я не нажму кнопку "Кик" на сервере, чтобы "кикнуть" клиента.

Ошибка при приеме потока сервера после исключения первого человека, присоединившегося из трех:

java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
    at java.util.ArrayList.rangeCheck(ArrayList.java:604)
    at java.util.ArrayList.get(ArrayList.java:382)
  > at networktest.Server$3.run(Server.java:186)
    at java.lang.Thread.run(Thread.java:722)

Указанная строка — это ois = new ObjectInputStream, где я получаю тип данных в потоке приема сервера. Сервер отлично кикает первого человека, но также удаляет второго из списка с ошибкой java.lang.ClassCastException.

сервер получает:

private static Thread receive = new Thread()
{
    @Override
    public void run()
    {
        ObjectInputStream ois;

        while (true)
        {
            for (int i = 0; i < list_sockets.size(); i++)
            {
                try
                {
                    ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
                    int receive_state = (Integer) ois.readObject(); // receive state

                                            ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
                    byte datatype = (byte) ois.readObject(); // receive datatype

                                            if(datatype == 2){
                                                ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
                                                ChatLine chatLine = (ChatLine) ois.readObject(); // receive ChatLine
                                            } else if (datatype == 0){
                                                ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
                                                DataPackage dp = (DataPackage) ois.readObject(); // receive dp
                                                list_data.set(i, dp);
                                            }

                    if (receive_state == 1) // Client Disconnected by User
                    {
                        disconnectClient(i);
                        i--;
                    }
                }
                catch (Exception ex) // Client Disconnected (Client Didn't Notify Server About Disconnecting)
                {
                                        System.err.println("Error @ receive:");
                                        ex.printStackTrace();
                    disconnectClient(i);
                    i--;
                }
            }
                            try {
                                this.sleep(3);
                            } catch (InterruptedException ex) {
                                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                            }
        }
    }
};

пользователь отправил:

Thread send = new Thread()
{
    public void run()
    {
        ObjectOutputStream oos;
        byte datatype = 0;

        while (connected){
            if (socket != null){
                try {
                    DataPackage dp = new DataPackage();
                    dp.x = Client.player.x;
                    dp.y = Client.player.y;
                    dp.username = username;
                    dp.charType = charType;
                    dp.walking = (byte)Client.player.walking;
                    if (Client.outputChatLine.line != null)
                        datatype = 2;
                    else {
                        datatype = 0;
                    }

                    oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(Integer.valueOf(Client.this.state)); // send state

                    oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(Byte.valueOf(datatype)); // send datatype

                    if (datatype == 2)
                    {
                        oos.reset();
                        oos.writeObject(Client.outputChatLine);
                        Client.outputChatLine = new ChatLine();
                    } else {
                        oos = new ObjectOutputStream(socket.getOutputStream());
                        oos.writeObject(dp);
                    }

                    if (Client.this.state == 1) {
                        connected = false;
                        socket = null;

                        JOptionPane.showMessageDialog(null, "Client Disconnected", "Info", 1);
                        System.exit(0);
                    }

                }
                catch (Exception ex){}
            }
            try {
                this.sleep(2);
            } catch (InterruptedException ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
};

метод отключения клиента:

public static void disconnectClient(int index)
{
    try
    {
        list_clients_model.removeElementAt(index);
        list_client_states.remove(index);
        list_data.remove(index);
        list_sockets.remove(index);
    }
    catch (Exception ex) {}
}

Кто-нибудь знает, как это решить?


person Stefan Schouten    schedule 13.09.2012    source источник
comment
Во-первых, исправьте блок catch в disconnectClient. Никогда не ловите голые Exception и просто игнорируйте их. Затем скажите нам, какая строка 186...   -  person Jon Skeet    schedule 14.09.2012


Ответы (1)


Похоже, вы ожидаете, что какой-то другой поток заполнит некоторые данные в list_sockets, пока вы sleep(3) делаете. Но у вас нет синхронизации, чтобы это происходило только во время сна.

С таким же успехом может случиться так, что другой поток обновляет list_sockets одновременно с вашим собственным потоком, вызывающим list_sockets.get(i). И реализация ArrayList почти наверняка не написана так, чтобы два разных метода выполнялись одновременно в двух разных потоках. Например, другой поток может быть в середине изменения размера резервного массива, когда вы пытаетесь прочитать элемент, и тогда может произойти что-то неладное, включая ошибку, которую вы видите.

Вам нужно узнать о синхронизации между потоками. По крайней мере, вам нужно synchronized блоков для защиты доступа к общим структурам данных. И пока вы в этом; посмотрите на wait/notify или какие-нибудь инструменты параллелизма более высокого уровня, чтобы избавиться от этого ужасного 3-миллисекундного цикла опроса - вместо этого пусть поток, который отбрасывает работу в список, явно пробуждает рабочий поток.

person hmakholm left over Monica    schedule 13.09.2012
comment
Я только что обнаружил, что ответ ajon действительно не был моим ответом. Поскольку я не так далеко продвинулся в программировании сокетов, как я думал, не могли бы вы дать мне небольшую подсказку, с чего начать? - person Stefan Schouten; 14.09.2012
comment
@StefanSchouten: Как вы выучили Java? В вашем учебнике (или любом другом) должна быть хорошая глава о потоках и синхронизации, которую вы должны начать с чтения. - person hmakholm left over Monica; 14.09.2012
comment
Промежуточный java, я учился там, где я учусь. Этому сокету я научился на уроке на YouTube. Даже если я думаю, что у меня есть навык, на самом деле это не так. Но у меня есть учебник Java — руководство для начинающих. В нем есть глава многопоточного программирования, это то, где я должен быть? - person Stefan Schouten; 14.09.2012
comment
@StefanSchouten: я не знаю конкретную книгу, но это определенно звучит как название главы, которую вы должны внимательно прочитать, прежде чем приступать к программированию с использованием более чем одного потока. - person hmakholm left over Monica; 14.09.2012
comment
Ok. Большое спасибо за ваше время! И... я чувствую себя виноватым, что не подумал проверить книгу перед публикацией. У меня есть - почему бы и не использовать :) так что извините. - person Stefan Schouten; 14.09.2012
comment
@StefanSchouten: не чувствуй себя виноватым; вы не могли знать, что нужно проверить книгу, прежде чем узнали, что проблема, с которой вы столкнулись, была проблемой параллелизма - и для их выявления требуется некоторый опыт. И помните, что тема общеизвестно сложна и в ней легко ошибиться, поэтому не довольствуйтесь решением, пока не будете уверены, что знаете, почему оно работает. - person hmakholm left over Monica; 14.09.2012