-2

I'm trying to create a multithreaded broadcasting server. I have one class called ClientHandler that implements Runnable, and each instance of this class handles a single connection to a client. My server has a HashSet called connectedClients, that stores references to objects of the type Socket. So I want this ClientHandler to handle messages to the server, but I want the server to handle messages that goes to the clients, since I can easily just iterate over all of the clients and send one message to each.

I successufully made this server a working echo server. This means that now when I'm reworking it to be able to broadcast messages, I'm sure that everything in the server works except for these two methods:

  • The run()-method of ClientHandler:

    public void run() {
        try (
            BufferedReader in =
                new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream()));
    
        ) {
            String inputLine;
            while((inputLine = in.readLine()) != null) {
                chatServer.broadcastMessage(inputLine);
            }
    
            try {
                in.close();
            } catch (IOException e) {
                System.err.println("Couldn't close input stream");
            }
        } catch(IOException e) {
            System.err.println("Got an IOException in ClientHandler");
        }
    }
    

    This method just waits for input from the client it's handling, and then sends the input to the server using this method:

  • broadcastMessage(String message):

    public void broadcastMessage (String message) {
    
        Iterator<Socket> iterator = this.connectedClients.iterator();
    
        while (iterator.hasNext()) {
            Socket currentSocket = iterator.next();
            try (                   
                PrintWriter out = 
                    new PrintWriter(currentSocket.getOutputStream(), true);
            ) {
                out.println("Server: " + message);
    
                out.close();
    
            } catch(IOException e) {
                System.err.println("Got an IOException error while reading or writing from/to client");
            }
        }
    }
    

    This method belongs to the server class, and to be able to use this method I pass along a reference to the instance of the server class when I create a ClientHandler inside the server class, like this: new ClientHandler(clientSocket, this)).start();

I can send one message to the server from a client and get a response, but instantly afterwards I get my "Got an IOException in ClientHandler"-error and the program shuts down. Why?

And is this thinking the correct way to implement what I'm trying to implement?

Thanks in advance. Please tell me if I've accidentally left out some important piece of information.

Edit: Here's the stack trace from the IOException:

java.net.SocketException: socket closed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
    at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
    at sun.nio.cs.StreamDecoder.read(Unknown Source)
    at java.io.InputStreamReader.read(Unknown Source)
    at java.io.BufferedReader.fill(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at chat.ClientHandler.run(ChatServer.java:120)
    at java.lang.Thread.run(Unknown Source)
Jonatan Stenbacka
  • 1,824
  • 2
  • 23
  • 48
  • 1
    What protects `connectedClients` from getting modified while you're trying to iterate over it? – David Schwartz Sep 24 '14 at 11:22
  • 2
    Closing the streams will cause IOException in next try to use them. Mind that `Socket.getInputStream()` will get you the same instance of the stream, not a newly created one. – Fildor Sep 24 '14 at 11:23
  • @DavidSchwartz Well nothing, I guess. But I guess that's more of a consistency issue than what's causing my problem? But yes, that needs to be fixed as well. – Jonatan Stenbacka Sep 24 '14 at 11:31
  • @Fildor Okay, that's good to know. Although when removing the close()-line from these methods, I still get the same error. – Jonatan Stenbacka Sep 24 '14 at 11:37
  • @JonatanStenbacka `PrintWriter` and `BufferedReader` are _AutoCloseable_, see http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html – isalgueiro Sep 24 '14 at 11:38
  • You get *what* IOException? You need to produce a stack trace. Not just some vague message of you own devising. Don't do that. Log the actual exception, and edit it into your question. – user207421 Sep 24 '14 at 12:10
  • @isalgueiro Thanks man! That link was really helpful and what helped me the most in solving my problem. Sadly you can't mark a comment as the answer. – Jonatan Stenbacka Sep 24 '14 at 13:08
  • @EJP Thanks! I had no idea of what a strack trace was until now, I'll edit it into my question, even though it's solved now. Please tell me if it's not a "correct" strack trace. – Jonatan Stenbacka Sep 24 '14 at 13:10

1 Answers1

1

Closing the streams will cause IOException in next try to use them. Mind that Socket.getInputStream() will get you one and the same instance of the stream, not a newly created one.

Make sure, InputStream, OutputStream and the Socket itself is closed only when you are done communicating.

Fildor
  • 14,510
  • 4
  • 35
  • 67
  • Thanks! I had no idea of `Socket.getInputStream()` only giving me an instance of the stream. Removing my close-statements as well as not using the "try-with-resources"-statement, hence preventing the stream from closing too early, did the trick. – Jonatan Stenbacka Sep 24 '14 at 13:16