1

It's my first time working with sockets, in order to get a better understanding of what's going on I decided to build a client server chat application which can support several users.

At first, I used DataInputStream / DataOutputStream to communicate and everything works well. But I would like to switch to an ObjectStream and that's where the problem occurs. Once I replace all the DataInputStream / DataOutputStream by ObjectInputStream / ObjectOutputStream, I'm no longer able to print the retrieved data.

This is the code that I used before, which works (DataStream) :

SERVER:

       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();
        }

CLIENT:

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();
}

And this is how I tried to perform the same idea with ObjectStream :

SERVER:

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();
}

CLIENT:

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();
        }

I feel like it has something to do with this if statement if (in.available() > 0) but I cannot say precisely what's going on.

Vincent Passau
  • 814
  • 8
  • 22
Terchila Marian
  • 2,225
  • 3
  • 25
  • 48
  • try adding flush/close after ``outputParticularClient.writeObject(input);`` (the stream is never closed) – spi Jun 07 '19 at 11:46
  • I've tried that, but is still not working. The reason why I leave them open is because they are suppose to communicate as long as the application runs. Once it's closed the listeners are closed as well. – Terchila Marian Jun 07 '19 at 11:51
  • then flush should be enough. – spi Jun 07 '19 at 11:52
  • I've tried with flush on both client and server and I don't get any response from both of them. – Terchila Marian Jun 07 '19 at 11:53
  • after reading your code a bit more carefully, it seems nobody starts by sending data. Both of your methods start by "readObject". The client must start by sending some data. – spi Jun 07 '19 at 12:00
  • As I said in the first place, I got not previous experience with this so if possible could you explain me please what's the purpose of starting by sending some data and how could that solve my problem because I don't follow. – Terchila Marian Jun 07 '19 at 12:08
  • In your first example (with Data stream), you wrote the line ``out.writeUTF("HI FROM SERVER");``: there is nothing like that in your ObjectStream version. Like in any real world discussion, if two person are waiting for the other to start to say something well... you are gonna wait a long time :) – spi Jun 07 '19 at 12:09
  • Okey so I've created a new Message instance and I tried to send it from the server to the client once connected and it couldn't retrieve it. As I said in the question I think it has something to do with in.available, I've took a look over the doc and it says that available() Returns the number of bytes that can be read without blocking. But idk what they try to mean by that – Terchila Marian Jun 07 '19 at 12:14
  • 2
    'I'm no longer able to print the retrieved data' is not a problem description. Your use of `available()` is pointless. – user207421 Jun 07 '19 at 12:14
  • @user207421, No is not. Without it I get this error: java.io.StreamCorruptedException: invalid type code: AC at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1622) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430) at com.terkea/com.terkea.controller.ClientController.lambda$createClient$1(ClientController.java:79) – Terchila Marian Jun 07 '19 at 12:18
  • 1
    StreamCorrupted has nothing to do with the use of ``available()``. You must be doing something else wrong. Still, flush is most likely required for the data to be sent (else, flush occurs only when the buffers are full or when the streams are closed). – spi Jun 07 '19 at 12:42
  • By the way ``if (input.equals(null)){``is always false, or will always die with a NullPointerException. Write "``if(input==null)``" instead. – spi Jun 07 '19 at 12:48
  • @spi I got rid of that input.equals statement and as I said earlier once I delete the available statement I get a StreamCorrupted Exception – Terchila Marian Jun 07 '19 at 12:49
  • 1
    As @user207421 said, the use of `available` is pointless. The `StreamCorruptedException` should be caused by re-creating new ObjectOutputStreams as described in the answer to this [question](https://stackoverflow.com/questions/2393179/streamcorruptedexception-invalid-type-code-ac). – Stefan Zobel Jun 07 '19 at 15:25

1 Answers1

3

available() doesn't do what you may think it does and it is almost never useful in production code (and that's particularly true for ObjectInputStream). The reason you don't receive any data is in fact that in.available() always returns 0 as you already suspected.

As noted in the comments, the StreamCorruptedException is caused by writing to an existing ObjectInputStream that has already been written to using another instance of ObjectOutputStream. Cf. the answer StreamCorruptedException: invalid type code: AC for further explanation.

Here is some quick & dirty example code that has a server echoing the messages from two clients. It's not clean but it may give you an idea how to approach your problem:

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");
   }
}
Anlon Burke
  • 397
  • 4
  • 12