【问题标题】:The java socket chatting codes throw java.net.SocketException: Socket closedjava socket聊天代码抛出java.net.SocketException: Socket closed
【发布时间】:2019-01-11 22:59:25
【问题描述】:

我尝试使用 Java 套接字 API 制作基本的 Java 聊天应用程序。但是当客户端终止时,它会抛出以下异常,

java.net.SocketException: 套接字关闭

我的代码

服务器

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

客户

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

当生成的客户端以“/exit”消息终止时,BufferedReader.readLine() 行会抛出如下异常

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)

我认为即使套接字已经关闭,BufferedReader 流仍会尝试读取行消息。

但我不知道当客户端终止时何时以及如何关闭 BufferedReader Stream 和 Socket 连接。

我完全被这部分困住了。有什么想法吗?

【问题讨论】:

  • 听起来不错。你还期望发生什么?至于处理它,为什么不明确地捕获它,然后自己清理(或让finally处理程序运行)?
  • 感谢您的回复。当客户端终止时,我会尽力消除此异常。我想知道如何在服务器连接终止时同时关闭客户端的套接字。问题是即使服务器端的套接字关闭,客户端的流仍然存在。最好的问候。

标签: java sockets


【解决方案1】:

这是因为当您调用readLine() 时,套接字已关闭。您可以先检查消息,如果消息是\exit,则先停止inputThread(使用标志或其他方式),然后将\exit 发送到服务器。简而言之,检查/exit -> 停止inputThread -> 将/exit 发送到服务器。

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

【讨论】:

  • 我认为你可以展示代码来帮助约瑟夫(和我)理解你的答案