java - 为什么我的套接字没有在服务器端关闭?

标签 java sockets tcp

我正在尝试制作一个简单的文本编辑器,可以同时在多个终端之间共享。我有一个服务器等待新用户,当用户进入共享编辑器时,它只是开始等待输入字符。

public class Server {
    public static final int PORT = 8080; 
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(PORT);

        while (true) {
            Socket socket = ss.accept();
            System.out.println("A new user entered the sever");
            new Thread(() -> serve(socket)).start();
        }
    }

    private static void serve(Socket socket) {
        try {
            while (!socket.isClosed() && !socket.isInputShutdown()) {
                System.out.println("hey " + socket.isClosed() + " " + socket.isInputShutdown());
                System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

当用户关闭编辑器时,客户端的套接字将关闭。然而,服务器端的套接字并没有关闭,服务器开始在“等待输入”循环中无限循环。

Client 是一个包含以下方法的单例,在打开和关闭编辑器时调用。

public static void init() {
        try {
            if (socket == null) socket = new Socket(HOST,Server.PORT);
        } catch (IOException e) {
            e.printStackTrace();
            kill();
            throw new Error(e.getMessage());
        }
    }

    public static void kill() {
        Check.notNull(socket);
        try {
            SocketUtil.terminateCommunication(socket);
            System.out.println(socket.isClosed());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

最后,这是两个类中使用的实用方法(在 SocketUtil 中):

public static void terminateCommunication(Socket socket) throws IOException {
    socket.shutdownInput();
    socket.shutdownOutput();
    socket.close();
}

public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
    char[]            bytes = new char[nBytes];
    InputStreamReader isr   = new InputStreamReader(socket.getInputStream());
    isr.read(bytes);
    return bytes;
}

知道为什么在 Client 被杀死后服务器端的套接字没有关闭吗?

最佳答案

Javadoc 中并不太清楚,但是当您显式调用 close() 时,isClosed() 仅返回 true套接字(请参阅消息来源以确认这一点)。您应该检查异常和 read() 的返回值。如果您在尝试读取(或写入)时读取 -1 或捕获 IOException,则本质上意味着另一端已关闭连接,因此您也应该关闭您的套接字(最好在 finally block 中关闭),然后您就完成了该特定连接。您不会在 receiveBytes() 中检查 -1,但您确实应该检查。如果您想将这两种可能性合并为一种,也许可以抛出一个 EOFException() ,这样堆栈上的代码(在 serve() 中)就不必计算到底发生了什么:

public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
    char[]            bytes = new char[nBytes];
    InputStreamReader isr   = new InputStreamReader(socket.getInputStream());
    if (isr.read(bytes) == -1)
        throw new EOFException();
    return bytes;
}

IOException 规则中的一个异常(exception)(抱歉是双关语)是 SocketTimeoutException。如果您收到此消息,则连接仍然有效,您也可以重试 read()。但我相信为了获得这些,您必须在某个地方调用 Socket.setSoTimeout() ,如果没有,那么您可能不应该担心 SocketTimeoutException 。 .

您还应该注意,read() 有时可能会返回部分读取(即小于bytes.length)。如果 receiveBytes() 准确读取 nBytes 很重要(这可能是,因为您永远不会返回实际读取的字符数),那么您应该在循环中调用它,像这样:

    int pos = 0;
    while (pos < bytes.length) {
        int l;
        if ((l = isr.read(bytes, pos, bytes.length - pos)) == -1) {
            throw new EOFException();
        }
        pos += l;
    }

我知道这很麻烦,这正是许多开发人员创建像 receiveBytes() 这样的实用方法的原因。

关于java - 为什么我的套接字没有在服务器端关闭?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30014460/

相关文章:

c - 使用 LD_PRELOAD 覆盖连接调用

c - 如何在 C 中将客户端的 IP 存储在数组中(套接字编程)

c - 如何连接到 c 中的 bit torrent tracker

c - 在用 C 编写的 Linux TCP/IP 服务器中处理 Ctrl-C

java - JList 项目删除后回来?

c# - Azure网站不断抛出错误 "An attempt was made to access a socket in a way forbidden by its access permissions"

java - 传输了多少数据?

Azure 容器实例 udp/tcp

java - Java 中的后缀数组压缩

java - Java中如何从某个偏移量处读取文件?