Yige Song
Yige Song

Reputation: 373

Java: Socket Closed Error while using thread

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

Upvotes: 1

Views: 758

Answers (1)

Daniel
Daniel

Reputation: 1460

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

Upvotes: 2

Related Questions