1

I am building a multithread chat server.

The multi-threaded server (Manager Class) could serve many clients. It receives a message from a client, and broadcast to all clients.

The client (Peer Class) have two threads - SendThread for sending a message to the server. ReceiveThread to listen to the server broadcast.

However, while running the client program, it catches the exception and says that socket closed.

My code for the server class is below:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Manager {
    public int port;
    public ArrayList<Socket> clients;

    public Manager(int port) throws IOException {
        this.port = port;
        this.clients = new ArrayList<>();

        try (ServerSocket server = new ServerSocket(port)){
            System.out.println("Waiting for client connection-");
            while (true){
                Socket client = server.accept();
                clients.add(client);
                System.out.println("Client applies for connection");

                Thread t = new Thread(new serverClientThread(client));
                t.start();
            }
        }
    }

    public class serverClientThread implements Runnable {
        private Socket client;

        public serverClientThread(Socket client){
            this.client = client;
        }

        @Override
        public void run() {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(this.client.getInputStream()));

                while (true){
                    // read
                    String line = reader.readLine();
                    if (line != null){
                        System.out.println("I received "+line);
                        // write

                        // broadcast
                        broadcast("I received " + line);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    // broadcast the message to all clients
    public synchronized void broadcast(String message) throws IOException {
        for (Socket client:this.clients){
            if (client.isClosed()){
                continue;
            }
            try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))){
                writer.write("I received " + message);
                writer.newLine();
                writer.flush();
            }
        }
    }
}

The code of the client class is below:

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Peer {
    public String hostname;
    public int port;

    public Peer(String hostname, int port){
        this.hostname = hostname;
        this.port = port;


        try (Socket socket = new Socket(hostname, port)){
            // create writer
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            Thread t1 = new Thread(new SendThread(writer));
            Thread t2 = new Thread(new ReceiveThread(reader));

            t1.start();
            t2.start();

        } catch (UnknownHostException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    public class SendThread implements Runnable{
        private BufferedWriter writer;

        public SendThread(BufferedWriter writer){
            this.writer = writer;
        }

        @Override
        public void run() {
            Scanner sc = new Scanner(System.in);
            while (true) {
                System.out.print("Enter a String: ");
                String str = sc.nextLine();
                // send to server
                if (str != null){
                    try {
                        this.writer.write(str);
                        this.writer.newLine();
                        this.writer.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public class ReceiveThread implements Runnable{
        private BufferedReader reader;

        public ReceiveThread(BufferedReader reader){
            this.reader = reader;
        }

        @Override
        public void run() {
            while (true){
                String res = null;
                try {
                    res = this.reader.readLine();
                    if (res != null){
                        System.out.println("Server response: "+ res);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    System.exit(-1);
                }
            }
        }
    }
}

The error message is:

java.net.SocketException: Socket closed
        at java.base/java.net.SocketInputStream.socketRead0(Native Method)
        at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168)
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
        at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)
        at java.base/java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.base/java.io.BufferedReader.readLine(BufferedReader.java:326)
        at java.base/java.io.BufferedReader.readLine(BufferedReader.java:392)
        at Peer$ReceiveThread.run(Peer.java:86)
        at java.base/java.lang.Thread.run(Thread.java:834)

It occurs in ReceiveThread in the Peer class.

Any bits of help is appreciated. Thank you!

Yige

2
  • Can you show the full error stack trace. Commented May 9, 2021 at 13:37
  • @sadsad yes, please see the updated question. Commented May 9, 2021 at 13:42

1 Answer 1

2

Since you are using a try-with-resources, the socket is automatically closed immediately after you start t1 and t2.

You can think of

try (Socket socket = new Socket(hostname, port)){
  // [...]
  t1.start();
  t2.start();
}
// 

like this:

Socket socket;
try {
  socket = new Socket(hostname, port)
  // [...]
  t1.start();
  t2.start();
} catch (/* [...] */) {
} finally {
  if (socket != null) {
    socket.close(); // <- here the socket is closed
  }
}

And since the thread is running in the background, t1.start() does not wait until thread-1 has finished -> the socket is closed.

Without try-with-resources:

public class Peer {

  private Socket socket;
  // [...]

  public Peer(String hostname, int port) {
    // [...]
    try {
      this.socket = new Socket(hostname, port);
      // [...]
    } catch (UnknownHostException | IOException ex) {
      ex.printStackTrace();
      System.exit(-1);
    }
  }

  // Call this method when your program exits
  public void close() {
    if (this.socket != null) {
      this.socket.close();
    }
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, Daniel. So is there are a way to avoid try-with-resources in my case?
Yes, I have edited my answer for your case

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.