Java服务器套接字连接重置

3
当我的第二个客户端连接到我的服务器时,我收到了以下错误信息:
Exception in thread "main" java.net.SocketException: Connection reset
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:200)
    at java.base/java.io.DataInputStream.readLine(DataInputStream.java:518)
    at Main.main(Main.java:24)

我不知道我做错了什么。

第一个客户端正常工作。

我的代码:

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

public class Main {
    public static void main(String[] args) throws IOException {
         ServerSocket serverSocket = new ServerSocket(50505);;
         Socket socket;
         while (true) {
             socket = serverSocket.accept();
             while(socket.isConnected()) {
                 String v;
                 DataInputStream in;
                 InputStream in_sock;
                 in_sock = socket.getInputStream();
                 in = new DataInputStream(in_sock);
                 v = in.readLine();
                 System.out.println(v);
                 OutputStream output = socket.getOutputStream();
                 DataOutputStream out = new DataOutputStream(output);
                 out.writeChars("123\n");
                 out.writeChars("123\n");
                 out.writeChars("123\n");
             }
         }
    }
}

PS:我如何在stackoverflow上分享错误?例如代码?


尝试在while循环内部声明你的Socket,而不是在外部声明。虽然我不知道你如何期望多个客户端同时连接到运行在单个线程上的服务器。 - Jacob G.
1
很抱歉,这个问题对我来说太宽泛了,无法在评论中回答。如果您想让多个客户端同时连接,您需要使用多个线程和/或非阻塞I/O。 - Jacob G.
1
我应该再提一个问题吗?顺便说一下,非常感谢你。 - Wiktor Janecki
刚才我认为它检查客户端是否与服务器断开连接。我没有检查这个,因为它没有显示任何错误并且代码可以运行。 - Wiktor Janecki
你在循环内部声明了应该在循环之前声明的东西,但主要问题在于你的客户端没有关闭它的套接字,因此出现了重置。另外,你没有检测到流的结束,并且从未关闭套接字。readLine() 在那一点返回 null,此时你应该关闭套接字并停止读取。isConnected() 不是流结束的有效测试。 - user207421
显示剩余7条评论
2个回答

5

就像其他人所说,最大的问题在于您的服务器由于缺乏多线程支持只能接受一个连接。

现在您的服务器正在等待连接到指定的端口。

socket = serverSocket.accept();

在socket仍然连接的情况下,从socket中读取一行数据并将其打印到System.out,然后在循环中写回socket。现在出现问题了,因为下次客户端尝试连接到您的serverSocket时,它无法接受连接,因为您的代码被困在从一个socket读取和写入数据的循环中。

解决方法是像Milen上面提到的那样引入多线程。

您的代码应该类似于这样。

public class Main {
    public static void main(String[] args) throws IOException {
         ServerSocket serverSocket = new ServerSocket(50505);
         while (true) {
             Socket socket = serverSocket.accept();
             SocketHandler h = new SocketHandler(socket);
             h.start();
         }
    }
}

public class SocketHandler extends Thread{

    Socket clientSock;
    public SocketHandler(Socket sock){
        clientSock = sock;
    }

    public void run(){
        while(clientSock.isConnected()) {
            String v;
            DataInputStream in;
            InputStream in_sock;
            in_sock = socket.getInputStream();
            in = new DataInputStream(in_sock);
            v = in.readLine();
            System.out.println(v);
            OutputStream output = socket.getOutputStream();
            DataOutputStream out = new DataOutputStream(output);
            out.writeChars("123\n");
            out.writeChars("123\n");
            out.writeChars("123\n");
        }
    }
}

现在这段代码有一个循环来接受连接并为每个连接生成一个新线程!

我建议查看一些内容。首先,如果您查看ServerSocket的Java文档,您将看到这个构造函数:

ServerSocket(int port, int backlog)
Creates a server socket and binds it to the specified local port number, with the specified backlog.

积压队列(backlog)是指服务器套接字可以持有和存储的传入连接数量,直到它们能够被接受。如果客户端在您的接受循环正在创建套接字处理程序时连接,则这非常有用。建议将其设置为您预期的最大客户端数。

其次,请查找Javadocs中的Thread类。要成功扩展Thread类,您需要重写run()方法。此方法是线程将执行的方法。要生成线程,您需要调用ThreadObject.start()。您可以将start()视为仅调用run()。


你的代码由于异常处理不足而无法编译;isConnected() 不是流结束的有效测试;应该在循环外构建流;而且你没有关闭套接字。 - user207421
是的,你说得对!我刚试图调整他们的代码结构。为了准确检查流的结束,你需要让while循环检查从readline返回的null值。当该null值被返回时,你应该关闭套接字。异常处理错误应该在客户端处理程序的I/O部分,尽管可能还有更多需要异常处理的地方。如果你需要我编写可编译的代码,请告诉我,我很乐意这样做。 - Mataan P

2

来自Oracle的"编写Socket服务器端"教程:

最初的回答:

... the server can service them simultaneously through the use of threads—one thread per each client connection.

The basic flow of logic in such a server is this:

while (true) {
    accept a connection;
    create a thread to deal with the client;
}

The thread reads from and writes to the client connection as necessary.

在那个教程中,您还会找到指向服务器(KKMultiServer)和线程(KKMultiServerThread)样例实现的链接。"最初的回答"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接