当前位置: 首页 > 知识库问答 >
问题:

Java插座编程:连接复位,尽管关闭插座?

唐健
2023-03-14

我目前正在编写一个简单的TCP聊天客户端服务器应用程序,它与套接字一起工作。客户端连接到服务器,一旦被接受,就会创建一个新的工作线程来侦听客户端输入。连接到服务器(端口8818处的localhost)工作正常,但只要工作线程在登录java.net之后开始监听更多客户端输入。SocketException:引发连接重置(请参阅下面的堆栈跟踪)。我知道,此异常的一个可能来源可能是服务器或客户端未正确或强制关闭的套接字。因此,我的假设是,我没有正确处理客户端套接字的关闭,这会导致连接重置

我想实现的目标:工作者监听客户端输入,只要不为空,就会处理请求(例如简单登录),否则,套接字将关闭(参见下面的代码摘录)。我的客户机从服务器收到一条“登录成功”消息,表明我的handleLogin()函数可以工作,但在没有从客户机收到更多输入后,服务器似乎只是重置了,即使clientSocket。close()在while循环之后发出。

服务器.java

@Override
    public void run() {

        try {

            ServerSocket serverSocket = new ServerSocket(serverPort);

            while (true) {

                // Accept connection(s) from new chat client(s)
                System.out.println("SERVER: WAITING TO ACCEPT CLIENT CONNECTIONS ...");
                Socket clientSocket = serverSocket.accept();
                System.out.println("SERVER: CONNECTION ACCEPTED FROM: " + clientSocket);
                // Process client request in separate Thread
                WorkerThread worker = new WorkerThread(this, clientSocket);
                workerList.add(worker);
                worker.start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

辅助线程.java

 @Override
    public void run() {

        try {
            handleClientSocket();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


    private void handleLogin(OutputStream outputStream, String[] tokens) throws IOException {

        if (tokens.length != 3) {
            outputStream.write("LOGIN FAILED!\n".getBytes());
                return;
        }

        // Extract username and password from user input
        String username = tokens[1];
        String password = tokens[2];

        if (username.equals("anna") && password.equals("anna")) {

            outputStream.write("Login successful!\n".getBytes());

        } else {
            outputStream.write("Error logging in!\n".getBytes());
        }

    }

    private void handleClientSocket() throws IOException, InterruptedException {

        InputStream inputStream = clientSocket.getInputStream();
        this.outputStream = clientSocket.getOutputStream();

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String line;

        // THIS IS WHERE THE EXCEPTION OCCURS AFTER CLIENT HAS LOGGED IN SUCCESSFULLY
        while ((line = bufferedReader.readLine()) != null) {

            String[] tokens = StringUtils.split(line);

            if (tokens.length > 0 && tokens != null) {

                String command = tokens[0];

                // Evaluate the entered command and handle the request accordingly

                if ("login".equalsIgnoreCase(command)) {
                    handleLogin(outputStream, tokens);
                } 

                // process other commands ...  

            }
        }

        clientSocket.close();   // POSSIBLY WORNG WAY OF CLOSING THE CLIENT SOCKET?
    }

客户端.java

import java.io.*;
import java.net.Socket;

public class Client {

    private final String serverName;
    private final int serverPort;
    private Socket mySocket;
    private OutputStream serverOut;
    private InputStream serverIn;

    public Client(String serverName, int serverPort) {
        this.serverName = serverName;
        this.serverPort = serverPort;
    }

    private boolean connect() {

        try {
            mySocket = new Socket(serverName, serverPort);
            serverOut = mySocket.getOutputStream();
            serverIn = mySocket.getInputStream();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    private void login(String username, String password) throws IOException {
        String command = "login " + username + " " + password + "\n";
        serverOut.write(command.getBytes());
    }

    public static void main(String[] args) throws IOException {

        Client client = new Client("localhost", 8818);

        if (client.connect()) {                             
            System.out.println("Connection successful!");   
            client.login("anna", "anna");                   

        } else {
            System.err.println("Connection failed ...");
        }

    }

}

堆栈跟踪

java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:189)
    at java.net.SocketInputStream.read(SocketInputStream.java:121)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at WorkerThread.handleClientSocket(WorkerThread.java:70)
    at WorkerThread.run(WorkerThread.java:45)

当我用PUTTY测试我的服务器时(即通过连接到localhost,然后在PUTTY的终端中发出命令),一切都很好。

我对套接字编程还是相当陌生的,所以我可能在这里也有逻辑缺陷。任何帮助都将不胜感激,因为我真的很想继续这个项目。提前感谢!

共有1个答案

莫宝
2023-03-14

此异常的原因是,一旦连接了服务器,您就终止了客户端,但服务器仍从流中读取某些内容。发生异常。以下是我测试的代码:

    while (inputStream.available() != 0) {
        line = bufferedReader.readLine();
        String[] tokens = StringUtils.split(line);

        if (tokens.length > 0 && tokens != null) {

            String command = tokens[0];

            // Evaluate the entered command and handle the request accordingly

            if ("login".equalsIgnoreCase(command)) {
                handleLogin(outputStream, tokens);
            } 

            // process other commands ...  

        }
    }

在 while 循环中更改条件,以检查输入流是否仍可读取。

 类似资料:
  • 问题内容: 我正在使用Java套接字客户端。如果服务器仍连接到我的客户端,但是它不发送对我的消息的响应,则最终会出现读取超时异常。 在那种情况下,我想测试一下是否应该重新连接我的插座,或者只是保持插座可重复使用。 我使用这种情况: 但我似乎总是重新连接。当我记录布尔属性的值时,我看到以下内容: 已连接:true已关闭:true绑定:true 如何连接和关闭? TIA 问题答案: 该主题对此主题进行

  • 我正在通过蓝牙连接2台设备,当我按下一台设备上的注销按钮时,我想向另一台设备发送消息(告诉另一台设备也注销),关闭蓝牙连接,并关闭当前活动(即返回我的登录活动) 问题是我总是得到这个异常,这让我觉得我没有正确地关闭我的连接: 是从输入流读取的位置 当按下logout时,我基本上销毁了< code>MainActivity,并在< code>onDestroy()中停止了我的蓝牙服务: < code

  • 背景我正试图为我的java服务器创建一个android客户端应用程序,并让它们使用TCP套接字进行通信。 当我在android设备模拟器上运行我的应用程序时,它的工作原理是什么 当我在手机上运行该应用程序时,我无法正常工作,我在创建套接字时遇到连接超时。 异常 java.net.Connect异常: 无法连接到 /10.0.2.2 (端口 9111): 连接失败: ETIMEDOUT (连接超时)

  • 我正在运行一组tornado实例,它处理来自一小组keep-alive连接的许多请求。当我关闭服务器进行维护时,我希望优雅地关闭keep-alive请求,这样我就可以关闭服务器。有没有办法告诉客户“嘿,这个插座正在关闭”龙卷风?我环顾四周,只是刷新连接。

  • 问题内容: Java 类有两种方法,分别检查套接字是关闭还是连接。我想知道仅开放的TCP套接字与开放和连接的TCP套接字有什么区别,这与UDP有何不同。 问题答案: 为了把事情简单地说,这是 开放的 是要么等待连接或已与另一个成功连接的插座。当一个套接字被 关闭 ,这意味着这个套接字没有连接不再可用,而且它的资源已被释放。一个已连接的好,这意味着该套接字已连接到另一个。 这样可以 同时打开并连接。

  • 问题内容: 该程序连接到服务器,并且当服务器关闭连接时,如果我尝试重新连接,它会说: 如果我关闭客户端中的套接字,然后尝试重新连接,它会说:。 有没有办法在管道破裂后重新连接它而不创建新的套接字? 问题答案: 假设这是一个面向连接的套接字: 否。您必须关闭旧的插座并创建一个新的插座,