为什么我的套接字在Android上关闭?

时间:2014-08-13 12:36:29

标签: java android sockets

目标
我目前正在开发我的第一个“真正的”Android应用程序,它访问在我自己的集群上运行的服务器。基本功能完美无缺,但现在我想每5分钟实现一次自动重新连接。

问题
为此,我做了一个TimerTask,执行一个处理重新连接的方法。服务器输出告诉我重新连接工作正常,因为我可以看到“登录成功”。但是一旦重新连接任务执行,我在ADB输出中看到了这个异常:

08-13 14:15:18.805  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ java.net.SocketException: Socket closed
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at libcore.io.Posix.recvfromBytes(Native Method)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at libcore.io.Posix.recvfrom(Posix.java:141)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.net.PlainSocketImpl.read(PlainSocketImpl.java:489)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:241)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.io.InputStreamReader.read(InputStreamReader.java:231)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.io.BufferedReader.readLine(BufferedReader.java:397)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at net.dreamcode.android.dreampush.PushActivity$3.run(PushActivity.java:228)
08-13 14:15:18.810  19568-19617/net.dreamcode.android.dreampush W/System.err﹕ at java.lang.Thread.run(Thread.java:841)

很清楚它意味着什么,我不是网络编程和Java的新手。但在启动侦听器线程之前,我无法看到关闭套接字的重点 当然,在重新连接时,我关闭套接字,但我重新打开它,正如我已经说过的那样,通过身份验证令牌的登录工作也很完美,所以重新打开套接字也有效。

CODE
以下是该过程中涉及的方法(缩短的代码):

private void initServerConnection() {
    try {
        Thread serverConnectThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    socketToServer = new Socket();
                    socketToServer.connect(new InetSocketAddress("emerald.dream-code.net", 25565));

                    if (socketToServer.isConnected()) {
                        connectionState = true;
                    }
                }
                catch (Exception ex) {
                    connectionState = false;
                    ex.printStackTrace();
                }
            }
        });
        serverConnectThread.start();
        serverConnectThread.join();
    }
    catch (Exception ex) {
        ex.printStackTrace();
    }
}

public void doLoginProcedure(final String username, final String password) throws Exception {
    writer = new BufferedWriter(new OutputStreamWriter(socketToServer.getOutputStream()));
    reader = new BufferedReader(new InputStreamReader(socketToServer.getInputStream()));

    // Save the username for further usage
    PushActivity.username = username;

    // Write the login command in additional thread to have the GUI free of lags
    Thread loginThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                writer.write("LOGIN " + username + " " + password + "\n");
                writer.flush();

                // Read the response which is "failure" or "success" depending on the correctness of the login credentials
                String loginResponse = reader.readLine();
                if (loginResponse.equals("success")) {
                    // Set the login flag
                    isLoggedIn = true;

                    // Receive the login token for automatic logins in this session
                    authToken = reader.readLine();

                    // Get the old messages
                    messages.clear();
                    int messagesToReceive = Integer.parseInt(reader.readLine());

                    List<String> notifiableMessages = new ArrayList<>();

                    for (int i = 1; i <= messagesToReceive; i++) {
                        String messageFromServer = reader.readLine();
                        messages.add(messageFromServer);

                        if (i > readMessageCount) {
                            notifiableMessages.add(messageFromServer);
                        }
                    }

                    notifyUser(notifiableMessages);

                    readMessageCount = messages.size();
                }
                else {
                    isLoggedIn = false;
                    socketToServer.close();
                    initServerConnection();
                    // Notifiy user of failed login
                }
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    });
    loginThread.start();
    loginThread.join();

    if (isLoggedIn() && !isListenerRunning()) {
        displayMainContent();
        reconnectTask.startReconnectTask();
    }
}

private void displayMainContent() throws Exception {
    this.setContentView(R.layout.main);

    // Register the list view
    listView = (ListView) findViewById(R.id.listView);

    Button reconnectBtn = (Button) findViewById(R.id.button);
    reconnectBtn.setOnClickListener(new ReconnectButtonListener(this));

    ArrayAdapter<String> contentAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, messages);
    Log.d("SWAGGERINO", "" + messages.size());
    listView.setAdapter(contentAdapter);

    if (!this.isListenerRunning()) {
        this.startListener();
    }
}

private void startListener() {
    isListenerRunning = true;
    final Handler handler = new Handler();
    Runnable listenerRunnable = new Runnable() {
        @Override
        public void run() {

            try {
                String input;
                while (!(input = reader.readLine()).equals("exit")) {
                    if (!isListenerRunning()) {
                        break;
                    }

                    final String tmp = input;

                    handler.post(new Runnable() {
                        public void run() {
                            addListContent(tmp);

                            if (!getPushActivity().isInForeground()) {
                                List<String> notification = new ArrayList<>(1);
                                notification.add(tmp);
                                notifyUser(notification);
                            }
                        }
                    });
                }
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }

        }
    };

    Thread listenerThread = new Thread(listenerRunnable);
    listenerThread.start();
}

private void stopListener() {
    try {
        socketToServer.close();

        reader = null;
        writer = null;
        socketToServer = null;
    }
    catch (Exception ex) {
        ex.printStackTrace();
    }
}

当然还有处理重新连接的方法:

public void doRelogin() {
    stopListener();
    initServerConnection();

    try {
        doLoginProcedure(username, authToken);
    }
    catch (Exception ex) {
        ex.printStackTrace();
    }

    startListener();
}

ALREADY CHECKED
流程为:doLoginProcedure(),然后是startListener()。在doLoginProcedure()中,一切正常,因为用户登录服务器。所以我检查了服务器登录后是否关闭了连接,但事实并非如此。

我还在服务器上有一个清除任务,用于删除双重/非活动客户端,并检查服务器是否可能删除了错误的客户端,但情况并非如此。

我真的没有线索如何解决这个问题,并希望得到帮助。如果我错过了一些代码,请发表评论,我会添加它 提前致谢

2 个答案:

答案 0 :(得分:1)

我试图了解代码的逻辑......


但我注意到的一件事是你在几个地方做了这样的事情:

     Thread thread = new Thread(new Runnable(){ ... });
     thread.start();
     thread.join();

线程的这种用法毫无意义。您可以通过执行Runnable.run()方法的主体来实现完全相同的效果。

实际上,它比没有意义的更糟糕,因为如果run()方法抛出一些未经检查的异常,并未在run()方法本身中捕获并记录,那么该异常很容易发生被忽视。并且,可能可能会发生在这里......

但即使不是,冗余线程也很昂贵,并且使您的代码更复杂,更难阅读。


我唯一能想到的是客户端可能没有获得正确的登录响应行。这会导致它认为登录失败。

除此之外,我可以提出的最佳建议是在调试器中运行客户端代码或添加更多跟踪打印或日志记录。 (例如,查看Socket.isClosed()在不同点返回的内容......)

答案 1 :(得分:0)

此异常表示关闭了套接字。它并不表示对等方已断开连接。

因此,它是您代码中的逻辑缺陷。你确实至少在一点上关闭了套接字。

我建议您在执行此操作后将套接字设置为null。然后,此异常将成为NPE,您将 来解决此问题: - |

关闭套接字的输入流或输出流关闭另一个流和套接字。