Java, как читать с ObjectInputStream - PullRequest
1 голос
/ 07 июня 2019

Я впервые работаю с сокетами, чтобы лучше понять, что происходит, и я решил создать клиент-серверное приложение для чата, которое может поддерживать несколько пользователей.

Сначала я использовал DataInputStream / DataOutputStream общаться и все работает хорошо.Но я хотел бы перейти на ObjectStream, и именно здесь возникает проблема.Как только я заменю все DataInputStream / DataOutputStream на ObjectInputStream / ObjectOutputStream, я больше не смогу печатать восстановленные данные.

Это код, который я использовал ранее, который работает (DataStream):

СЕРВЕР:

       try {
            DataInputStream in = new DataInputStream(socket.getInputStream());
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());

            out.writeUTF("HI FROM SERVER");
            while (!socket.isClosed()) {
                try {
                    if (in.available() > 0) {
                        String input = in.readUTF();
                        for (ClientThread thatClient : server.getClients()){
                            DataOutputStream outputParticularClient = new DataOutputStream(thatClient.getSocket().getOutputStream());
                            outputParticularClient.writeUTF(input + " GOT FROM SERVER");
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

КЛИЕНТ:

try {
    socket = new Socket("localhost", portNumber);


    DataInputStream in = new DataInputStream(socket.getInputStream());
    new Thread(()->{
        while(!socket.isClosed()){
            try {
                if (in.available() > 0){
                    String input = in.readUTF();
                    System.out.println(getUserName() + " > " + input);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
} catch (IOException e) {
    e.printStackTrace();
}

И вот как я пытался реализовать ту же идею с ObjectStream:

СЕРВЕР:

try {
    ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());

    while (!socket.isClosed()) {
        try {
            if (in.available() > 0) {
                Message input;
                try {
                    input = (Message)in.readObject();
                    if (input.equals(null)){
                        System.err.println("SERVER RETRIEVED NULL OBJECT");
                    }
                    for (ClientThread thatClient : server.getClients()){
                        ObjectOutputStream outputParticularClient = new ObjectOutputStream(thatClient.getSocket().getOutputStream());
                        outputParticularClient.writeObject(input);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

КЛИЕНТ:

try {
            socket = new Socket(getHost(), portNumber);

            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
            new Thread(()->{
                while(!socket.isClosed()){
                    try {
                        if (in.available() > 0){
                            Message input = null;
                            try {
                                input = (Message)in.readObject();
                                if (input.equals(null)){
                                    System.err.println("CLIENT RETRIEVED NULL OBJECT");
                                }
                                System.out.println("CLIENT " + input.toString());
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        } catch (IOException e) {
            e.printStackTrace();
        }

Я чувствую, что это как-то связано с оператором if if (in.available() > 0), но я не могу точно сказать, что происходит.

1 Ответ

3 голосов
/ 07 июня 2019

available () не делает того, о чем вы думаете, и почти никогда не используется в производственном коде (и это особенно верно для ObjectInputStream). Причина, по которой вы не получаете никаких данных, заключается в том, что in.available() всегда возвращает 0, как вы уже подозревали.

Как отмечено в комментариях, StreamCorruptedException вызвано записью в существующий ObjectInputStream, который уже был записан для использования другого экземпляра ObjectOutputStream. Ср ответ StreamCorruptedException: неверный код типа: AC для дальнейшего объяснения.

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

public class SO56493162 {

   private static final class Message implements Serializable {
      private static final long serialVersionUID = 1L;
      private static int cnt = 0;
      private final int id;

      public Message(int id) {
         ++cnt;
         this.id = id;
      }

      public String toString() {
         return "Msg from " + id + " : " + cnt;
      }
   }

   private static final class Client implements Runnable {
      private InetSocketAddress addr = null;
      private int id = -1;

      Client(InetSocketAddress addr, int id) {
         this.addr = addr;
         this.id = id;
      }

      public void run() {
         int timeout = 3000;
         Socket s = null;
         try {
            s = new Socket();
            s.connect(addr, timeout);
            ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
            ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
            System.out.println("Client " + id + " connected");
            while (true) {
               Thread.sleep(new Random().nextInt(2000));
               Message hello = new Message(id);
               oos.writeObject(hello);
               oos.flush();
               Message reply = (Message) ois.readObject();
               System.out.println("Reply: " + reply.toString());
            }
         } catch (Exception e) {
            e.printStackTrace();
         } finally {
            try {
               s.close();
            } catch (Exception ignore) {
            }
         }
      }
   }

   private static final class Server implements Runnable {
      private ServerSocket sock = null;

      Server(ServerSocket sock) throws IOException {
         this.sock = sock;
      }

      public void run() {
         System.out.println("starting server");
         try {
            while (true) {
               final Socket client = sock.accept();
               System.out.println("connection accepted");
               Thread t = new Thread(new Runnable() {
                  @Override
                  public void run() {
                     try {
                        ObjectInputStream ois = new ObjectInputStream(client.getInputStream());
                        ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
                        while (!client.isClosed()) {
                           try {
                              Message input = (Message) ois.readObject();
                              oos.writeObject(input);
                              oos.flush();
                           } catch (EOFException eof) {
                              System.err.println("EOF!");
                              client.close();
                           }
                        }
                     } catch (Exception e) {
                        e.printStackTrace();
                     }
                  }
               });
               t.setDaemon(true);
               t.start();
            }
         } catch (Exception e) {
            e.printStackTrace();
         }
      }
   }

   public static void main(String args[]) throws IOException, InterruptedException {
      final int port = 9876;

      Thread ts = new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               new Server(new ServerSocket(port)).run();
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      });
      ts.setDaemon(true);
      ts.start();

      InetSocketAddress addr = new InetSocketAddress("localhost", port);

      for (int i = 0; i < 2; ++i) {
         Client cl = new Client(addr, i);
         Thread tc = new Thread(cl);
         tc.setDaemon(true);
         tc.start();
      }

      Thread.sleep(10000);
      System.err.println("done");
   }
}
...