Сокет не закрыт на Android

#java #android #android-activity #tcp #bufferedreader

#java #Android #android-активность #tcp #bufferedreader

Вопрос:

я написал простой TCP-клиентский класс на Java. Он используется для подключения к TCP-серверу, написанному на Python, и обработки входящих сообщений в новом потоке. Это выглядит так:

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

public class TCPTestClient {
    private String mHost;
    private int mPort;

    private Socket mSocket;
    private PrintWriter mWriter;
    private BufferedReader mReader;

    public TCPTestClient(String host, int port) {
        mHost = host;
        mPort = port;
    }

    public void connect() throws IOException {
        if (mSocket == null || mSocket.isClosed()) {
            mSocket = new Socket();
            mSocket.connect(new InetSocketAddress(mHost, mPort), 5000);
            mWriter = new PrintWriter(mSocket.getOutputStream());
            mReader = new BufferedReader(new InputStreamReader(mSocket
                    .getInputStream()));
            new Thread(new InputHandler(mReader, this)).start();
        }
    }

    public void close() throws IOException {
        if (mSocket != null amp;amp; !mSocket.isClosed()) {
            mSocket.close();
        }
    }

    class InputHandler implements Runnable {
        BufferedReader mReader;
        TCPTestClient mClient;

        public InputHandler(BufferedReader reader, TCPTestClient client) {
            mReader = reader;
            mClient = client;
        }

        public void run() {
            String line = null;
            try {
                while ((line = mReader.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  

TCP-сервер просто выводит «клиент подключен» и «клиент отключен» в стандартный вывод, если подключился или отключился новый клиент. Это отлично работает при запуске TCPTestClient в обычном java-приложении: соединение устанавливается при вызове connect() и закрывается при вызове close(), а ожидающая readLine() внутри InputHandler завершится ошибкой из-за исключения SocketException, в котором говорится, что сокет был закрыт ( java.net.SocketException: Socket closed ). Это поведение, которого я ожидал.

Но когда я запускаю этот код на Android, соединение не будет закрыто: readLine () по-прежнему блокируется без исключения SocketException, и сервер не показывает сообщение «клиент отключен».

Вот моя активность:

 import java.io.IOException;

import android.app.Activity;
import android.util.Log;

public class Foo extends Activity {
    private TCPTestClient mClient;

    private static final String TAG = "Foo";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.foo);

        mClient = new TCPTestClient("192.168.1.2", 3456);
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");

        try {
            mClient.close();
        } catch (IOException e) {
            Log.d(TAG, "Disconnect failed", e);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");

        try {
            mClient.connect();
        } catch (IOException e) {
            Log.d(TAG, "Connect failed", e);
        }
    }
}
  

Таким образом, при запуске / возобновлении действия соединение будет установлено. И когда пользователь нажимает на кнопку возврата, соединение должно быть закрыто. Однако вызываются onPause () и close(), но сокет не закрыт, потому что BufferedReader по-прежнему блокирует ожидание ввода. Вызов типа mReader.close() внутри блоков метода закрытия тоже.

Кто-нибудь знает, как исправить эту проблему, чтобы соединение было успешно закрыто при приостановке действия?

Комментарии:

1. Хорошо, я думаю, что нашел решение 🙂 Теперь я вызываю mSocket.shutdownInput() and mSocket.shutdownOutput() в своем close-методе. Это приводит к возврату readLine () и завершению моего InputHandler-Thread, а сервер печатает сообщение «отключено».

2. Для этого вам нужно только shutdownInput().

Ответ №1:

Как вы убедились, что клиентский сокет не закрыт?

Единственный надежный способ обнаружить закрытые соединения на стороне сервера — это записать данные и добавить частоту сердечных сокращений в протокол.

Комментарии:

1. Как я уже писал: сервер напечатает сообщение об отключении клиента в стандартный вывод. Более того, поток InputHandler должен завершиться, но он все еще зависает на readLine(). Так что, возможно, только потоки не закрыты должным образом. Это то, чего я хочу 🙂 Я думаю, что когда InputStream будет закрыт, сервер увидит это и напечатает ожидаемое сообщение. Но я понятия не имею, как закрыть поток, потому что, как упоминалось ранее, mReader.close() тоже блокируется. РЕДАКТИРОВАТЬ: И с «обычной Java» это работает так, как ожидалось 😉

Ответ №2:

Да, до версии 2.3 вы должны использовать mSocket.shutdownInput() и mSocket.shutdownOutput() для того, чтобы он выдавал исключение. Но в настоящее время в 2.2.2 у меня это не работает. : ( Все еще нахожу способ создать исключение.

Комментарии:

1. Вы когда-нибудь решали это? У меня были похожие проблемы, и мне пришлось увеличить minSdkVersion до 9 , потому что я не мог понять, как заставить его работать на Froyo и ниже… :/