Страницы

Поиск по вопросам

воскресенье, 8 марта 2020 г.

Java Десериализация ArrayList в цикле

#java #сокет #arraylist


Ребята, добрый день! 

Я новичок и выполняю следующее задание. Нужно построить клиент-серверное приложение,
где клиенты отправляют на сервер объекты периодически. сервер эти значения добавляет
в ArrayList и периодически рассылает клиентам. Проблема в том, что на стороне клиента
читается не весь ArrayList. а читается только первые несколько элементов. Помогите
разобраться. Коды и вывод информации ниже.

СЕРВЕРНАЯ ЧАСТЬ

             package sockets;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;


public class ServerSocketProg extends JFrame implements Runnable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    ArrayList users;
    private ArrayList clientObjectOutputStreams;
    private ArrayList stringArrayList = new ArrayList();
    private ServerSocket serverSock;
    private JTextArea ta_chat;
    private Path path;
    private BufferedWriter fileWriter;

    public ServerSocketProg() {
        initComponents();


        path = Paths.get("ServerLogger.txt");
        try {
            fileWriter = Files.newBufferedWriter(path, 
                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() 
    {
        clientObjectOutputStreams = new ArrayList();
        users = new ArrayList();  

        try 
        {
            serverSock = new ServerSocket(2222);
            while (true) 
            {
                Socket clientSock = serverSock.accept();                        
               
                ObjectOutputStream objectWriter = new ObjectOutputStream(clientSock.getOutputStream());
                clientObjectOutputStreams.add(objectWriter);

                Thread listenerObject = new Thread(new ClientObjectHandler(clientSock,
objectWriter));
                listenerObject.start();
                Thread intervalSender = new Thread(new SendArrayWithInterval());
                intervalSender.start();

                ta_chat.append("Got a connection. \n");
                fileWriter.write("Got a connection. \r\n");
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            ta_chat.append("Error making a connection. \n");
            try {
                fileWriter.write("Error making a connection. \r\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }   



    class ClientObjectHandler implements Runnable   
    {
           BufferedReader reader;
           Socket sock;
           ObjectOutputStream client;
           ObjectInputStream objectReader;

           public ClientObjectHandler(Socket clientSocket, ObjectOutputStream user)
throws IOException 
           {
                client = user;
                try 
                {
                    sock = clientSocket;
                    objectReader = new ObjectInputStream(sock.getInputStream());
               
                }
                catch (Exception ex) 
                {
                    ta_chat.append("Unexpected error... \n");                   
                    fileWriter.write(ex.toString());
                }

           }

           @Override
           public void run() 
           {
                String message;    

                try 
                {
                    while ((message = (String) objectReader.readObject()) != null) 
                    {
                        ta_chat.append("Received Object: " + message + "\n");
                        fileWriter.write("Received Object: " + message + "\r\n");
                        stringArrayList.add(message);                           
          
                    } 
                 } 
                 catch (Exception ex) 
                 {
                    ta_chat.append("Lost a connection. \n");
                    try {
                        fileWriter.write("Lost a connection\r\n");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    clientObjectOutputStreams.remove(client);
                 } 
           } 
        }

    class SendArrayWithInterval implements Runnable{
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(stringArrayList.size()>0){
                    ta_chat.append("Telling everyone Array...\n");                  
                    sendEveryoneObject();
                }
            }
        }
    }

    public void sendEveryoneObject() {
        Iterator it = clientObjectOutputStreams.iterator();

        while (it.hasNext()) 
        {
            try 
            {
                ObjectOutputStream outputWriter = (ObjectOutputStream) it.next();


                outputWriter.writeObject(stringArrayList);

                outputWriter.flush();

                ta_chat.append("Sending: " + stringArrayList.toString() + "\n");        
                ta_chat.setCaretPosition(ta_chat.getDocument().getLength());
                fileWriter.write("Sending: " + stringArrayList.toString() + "\r\n");

            } 
            catch (Exception ex) 
            {
                ex.printStackTrace();
                ta_chat.append("Error telling everyone. \n");
                try {
                    fileWriter.write("Error telling everyone. \r\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }




    }


    private void initComponents() {
        JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
        ta_chat = new javax.swing.JTextArea(40, 50);        
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Chat - Server's frame");
        setName("server");        

        jScrollPane1.add(ta_chat);
        jScrollPane1.setViewportView(ta_chat);
        add(jScrollPane1);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }); 

        setSize(400, 600);
        setVisible(true);
        pack();
    }



    public static void main(String[] args) {        
        Thread starter = new Thread(new ServerSocketProg());
        starter.start();            
    }
}


КЛИЕНТСКАЯ ЧАСТЬ: 

package sockets;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;


public class ClientsSocketsProgram extends JFrame implements Runnable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String address = "localhost";
    private int port = 2222;
    private Boolean isConnected = false;  
    private static int instance = 0;
    private Socket sock;
    private ObjectOutputStream objectWriter;
    private ObjectInputStream objectReader; 
    private JTextArea ta_chat;
    private Path path;

    private int incrementerToSend = 0;

    public ClientsSocketsProgram() {

        initComponentsClientFrame();
        connectToServer();       

        Thread incomeReader = new Thread(new IncomingReader());
        incomeReader.start();
    }   

    class IncomingReader implements Runnable
    {
        ArrayList inputArrayList;// = new ArrayList();
        @Override
        public void run() 
        {
            ArrayList here = new ArrayList();


            try 
            {
                while( true  ){

                    here =(ArrayList) objectReader.readObject();        
           

                    ta_chat.append("Object received: " + here.toString() + "\n "); 
                    ta_chat.setCaretPosition(ta_chat.getDocument().getLength());
             
                }
           }catch(Exception ex) {

               ex.printStackTrace();
           }
        }



    }

    @Override
    public void run() { 
        while(isConnected){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ta_chat.append("Sending " + incrementerToSend + "\n");

            sendObject(" " + incrementerToSend);
            ++incrementerToSend;
        }

    }




    private void initComponentsClientFrame() {

        JScrollPane jScrollPane = new javax.swing.JScrollPane();
        ta_chat = new javax.swing.JTextArea(20, 30);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle(" Chat - Client's frame");
        setName("client"); 


        jScrollPane.add( ta_chat );
        jScrollPane.setViewportView(ta_chat);
        add(jScrollPane);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                closeConnections();
            }
        }); 

        setSize(400, 600);
        setVisible(true);
        pack();
    }



    private void connectToServer() {
        if (!isConnected) 
        {
            try 
            {
                sock = new Socket(address, port);                
                objectReader = new ObjectInputStream(sock.getInputStream());
                objectWriter = new ObjectOutputStream(sock.getOutputStream()); 
                isConnected = true; 
            } 
            catch (Exception ex) 
            {
                ta_chat.append("Cannot Connect! Try Again. \n");

            }

        } else if (isConnected) 
        {
            ta_chat.append("You are already connected. \n");

        }
    }

    private void sendObject(String text) {

        try  {
            objectWriter.writeObject(text);
            objectWriter.flush();           
        } catch (Exception ex) {
            ta_chat.append("Message was not sent. \n");


            ex.printStackTrace();
        }

    }

    private void closeConnections(){
        if(isConnected){
            try{
                objectWriter.close();
                objectReader.close();
                sock.close();

                isConnected = false;
            }catch(Exception e){
                e.printStackTrace();
            }
        }       
    }





    public static void main(String args[]) 
    {
        Thread thread = new Thread(new ClientsSocketsProgram());
        thread.start();

    }


}


ВЫВОД СЕРВЕРНОЙ ЧАСТИ (ЗАПУЩЕНО 2 КЛИЕНТА): 

         Got a connection. 
Received Object:  0
Received Object:  1
Telling everyone Array...
Sending: [ 0,  1]
Received Object:  2
Got a connection. 
Received Object:  3
Received Object:  0
Received Object:  4
Received Object:  1
Telling everyone Array...
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Telling everyone Array...
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Received Object:  5
Received Object:  2
Received Object:  6
Received Object:  3
Telling everyone Array...
Sending: [ 0,  1,  2,  3,  0,  4,  1,  5,  2,  6,  3]
Sending: [ 0,  1,  2,  3,  0,  4,  1,  5,  2,  6,  3]


ВЫВОД КЛИЕНТСКАЯ ЧАСТЬ: 

Sending 0
Sending 1
Object received: [ 0,  1]
 Sending 2
Sending 3
Sending 4
Object received: [ 0,  1]
 Object received: [ 0,  1]
 Sending 5
Sending 6
Object received: [ 0,  1]
 Sending 7
Object received: [ 0,  1]
 Sending 8
Sending 9
Object received: [ 0,  1]
 Object received: [ 0,  1]
 Sending 10
Sending 11
Object received: [ 0,  1]
 Sending 12

    


Ответы

Ответ 1



Это происходит потому, что вы каждый раз передаете один и тот же объект ArrayList. ObjectInputStream/ObjectOutputStream позволяют передать объект вместе со всеми его полями, которые могут содержать ссылки на другие объекты, которые тоже могут на кого-то ссылаться. Поскольку может быть несколько ссылок на один объект, то при десериализации эти ссылки восстанавливаются, а не создается несколько копий. Т.е. writeObject видит, что этот объект он уже записывал, и кладет в поток ссылку, а readObject возвращает вам ссылку на ранее десериализованный объект. Чтобы этого избежать, вы можете использовать ObjectOutputStream.writeUnshared, чтобы основной объект считался уникальным. Еще можно после записи вызывать ObjectOutputStream.reset() чтобы последующие записи не учитывали ранее сериализованные объекты, и записывали их по-новой, а не в виде ссылок. Может быть стоит создавать новый ArrayList в sendEveryoneObject и рассылать его. Замечу, что вы очень вольно обращаетесь с многопоточностью. ArrayList не является потокобезопасной коллекцией, и не дает никаких гарантий при одновременной модификации (когда два клиента прислали вам сообщение, например). Вам нужно или самостоятельно обеспечить потокобезопасность через synchronized или Lock из java.util.concurrent, использовать Collections.synchronizedList (обратите внимание на способ использования итератора в документации метода), либо использовать какую-то коллекцию из java.util.concurrent (CopyOnWriteArrayList вам не очень подойдет, т.к. много модификаций, но для хранения сообщений может подойти одна из очередей). В последних двух случаях стоит скопировать данные перед отправкой клиентам в локально созданный список. При обращении к компонентам Swing следует использовать SwingUtilities.invokeLater, т.к. все обращения должны выполняться из Event Dispatch Thread. Создавать новый new Thread(new SendArrayWithInterval()) при каждом подключении клиента не нужно, у вас поэтому начинают дублироваться посылки сервера.

Комментариев нет:

Отправить комментарий