#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()
andmSocket.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 и ниже… :/