Joseph Hwang
Joseph Hwang

Reputation: 1421

The java socket chatting codes throw java.net.SocketException: Socket closed

I try to make basic Java Chatting Application with java socket APIs. But when the client is terminated, it throws the following Exception,

java.net.SocketException: Socket closed

My code

Server

package com.aaa.server;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class JavaChatServer {

    final static int Port = 10001; 

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ServerSocket serverSocket = null;
        Map<String, PrintWriter> clientMap = null;

        try {
            serverSocket = new ServerSocket(Port);
            clientMap = new HashMap<String, PrintWriter>();
            Collections.synchronizedMap(clientMap);

            while(true) {
                System.out.println("Waiting Connection ....");
                Socket socket = serverSocket.accept();

                ServerThread thread = new ServerThread(socket, clientMap);
                thread.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(!serverSocket.isClosed()) {
                    serverSocket.close();
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

}

class ServerThread extends Thread {
    private Socket socket = null;
    private BufferedReader br = null;

    private Map<String, PrintWriter> clientMap;
    private String id;

    public ServerThread(Socket socket, Map<String, PrintWriter> clientMap) {
        this.socket = socket;
        this.clientMap = clientMap;
    }

    @Override
    public void run() {
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));

            id = br.readLine();
            broadcast(id + " is connected");
            System.out.println("Connected User Id : " + id);
            clientMap.put(id, pw);

            String msg = "";

            while(true) {
                msg = br.readLine();

                if(msg.equals("/exit")) {
                    break;
                }

                broadcast(id + " : " + msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                clientMap.remove(id);
                broadcast(id + " is disconnected!!");

                if (br != null)
                    br.close();
                if(socket != null)
                    socket.close();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

    private void broadcast(String msg) throws Exception{
        Collection<PrintWriter> collection = clientMap.values();
        Iterator<PrintWriter> iterator = collection.iterator();

        while(iterator.hasNext()) {
            PrintWriter pw = iterator.next();
            pw.println(msg);
            pw.flush();
        }
    }
}

Client

package com.aaa.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class JavaChatClient {

    final static int Port = 10001;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Socket socket = null;
        PrintWriter pw = null;

        Scanner keyboard = new Scanner(System.in);
        String id = "";

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

            pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
            System.out.print("Welcome to Chat Room. Pls, type your ID : ");
            id = keyboard.nextLine();
            pw.println(id);
            pw.flush();

            ClientInputThread inputThread = new ClientInputThread(socket);
            inputThread.start();

            String msg = "";
            while (!msg.toLowerCase().equals("/exit")) {
                msg = keyboard.nextLine();

                if(!msg.trim().equals("")) {
                    pw.println(msg);
                    pw.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (keyboard != null)
                    keyboard.close();
                if (pw != null)
                    pw.close();
                if(socket != null)
                    socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

class ClientInputThread extends Thread {
    private Socket socket = null;

    public ClientInputThread (Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader br = null;

        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String msg = "";

            while(true) {
                if(socket.isClosed())
                    break;

                msg = br.readLine(); // This line throws SocketException
                System.out.println(msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

When the generated clients is terminated with "/exit" message, the BufferedReader.readLine() line throws the exception like below

java.net.SocketException: Socket closed
        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 com.aaa.client.ClientInputThread.run(JavaChatClient.java:80)

I think the BufferedReader stream still try to read line message even though the socket is already closed.

But I hava no idea when and how to close the BufferedReader Stream and Socket connection when the clients is terminated.

I am totally stuck on this part. Any ideas?

Upvotes: 0

Views: 1151

Answers (1)

zhh
zhh

Reputation: 2406

This is because when you are calling readLine(), socket is closed. You can first check the msg, if the msg is \exit, then first stop inputThread (use a flag or something else), then send \exit to the server. In a short, check /exit -> stop inputThread -> send /exit to server.

String msg = "";
while (!msg.toLowerCase().equals("/exit")) {
    msg = keyboard.nextLine();

    if (msg.toLowerCase().equals("/exit")) {
        // stop inputThread.
        // make sure inputThread is stopped.
    }

    if (!msg.trim().equals("")) {
        pw.println(msg);
        pw.flush();
    }
}

Upvotes: 0

Related Questions