Не удается прочитать сокет InputStream на желе Bean
У меня есть соединение сокетов TCP, которое хорошо работает на Android 2.3, но теперь сталкивается с некоторыми проблемами на Android 4.1.
Проблема в том, что метод InputStream.read() всегда возвращает -1 (без блокировки), например, соединение закрыто.
Создание сокета:
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket("c.whatsapp.net", 5222);
socket.setSoTimeout(3*60*1000);
socket.setTcpNoDelay(true);
Получение входных и выходных потоков и запись некоторых исходных данных:
InputStream inputStream = new BufferedInputStream(socket.getInputStream());
OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
outputStream.write(87);
outputStream.write(65);
outputStream.write(1);
outputStream.write(2);
outputStream.flush();
Затем это условие всегда проходит без блокировки:
int c = inputStream.read();
if (c < 0) {
Log.d(TAG, "End of stream");
}
Этот код работает в фоновом потоке. И он работал над Gingerbread.
Пытался использовать InputStreamReader и OutputStreamWriter вместо прямых потоков - никакого эффекта.
Ответы
Ответ 1
Я видел ту же самую ошибку раньше, хотя этот ответ может выглядеть оффтопным, дайте ему шанс и сообщите мне, если он сработает, по какой-то причине сокеты имеют странное поведение на желе, даже когда они работают полностью в нижнем андроиде версии, как я исправил эту проблему, было перемещение targetSdkVersion в желе bean, а также Project Build Target в свойствах Android проекта, не изменяло ни одной строки кода, просто так и по какой-либо причине делает трюк...
Надеюсь, что это поможет.
Привет!
Ответ 2
У меня была некоторая аналогичная проблема, где inputStream.read()
возвращался -1, и я не получал исключений. На самом деле сервер был отключен, и соединение сломалось. Я не тестировал его с разными версиями, только с 4.0.
Здесь Отчёт об ошибках Google об этом поведении.
К сожалению, статус ошибки кажется "закрытым" как невоспроизводимый.
Моя работа заключалась в том, чтобы интерпретировать -1 как закрытие сокета и недостижимого сервера. Когда вы пытаетесь восстановить соединение, вы получаете правильные ошибки.
Ответ 3
Друг,
попробуйте inputStream.readLine();
(i.e) DataInputStream.readLine();
(устаревший метод)
это сработало для меня...
Ответ 4
У меня была аналогичная проблема и исправлена с обходным способом, подобным этому
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private static class WatchDog implements Runnable{
private Thread thread = Thread.currentThread();
public void run() {
Log.d(LOG_TAG, "Interrupting read due to timeout");
thread.interrupt();
}
}
private void read(InputStream in, ByteBuffer bb, long waitTime) throws IOException {
int startingPos = bb.position();
long timeout = System.currentTimeMillis() + RESPONSE_TIMEOUT;
ScheduledFuture<?> watchdogFuture = executor.schedule(new WatchDog(), RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS);
try {
while(System.currentTimeMillis() < timeout && bb.hasRemaining()){ //workaround fixing timeout after 1ms
try{
int read = in.read(bb.array(), bb.position(), bb.remaining());
if(read > 0){
bb.position(bb.position()+read);
}
} catch(SocketTimeoutException e){}
if(bb.hasRemaining()){
Thread.sleep(5);
}
}
watchdogFuture.cancel(true);
} catch (InterruptedException e) {}
if(bb.hasRemaining()){
throw new SocketTimeoutException("Unable to read requested bytes: "
+ (bb.position()-startingPos) + "/" + (bb.limit()-startingPos)
+ " after " + (System.currentTimeMillis() - timeout + RESPONSE_TIMEOUT) + "ms");
}
}
Ответ 5
Использование BufferedReader и PrintWriter работает со всеми версиями для меня и чрезвычайно удобен для отправки и получения любых ваших желаний (даже строк JSON) через любой протокол связи. Попробуйте сохранить их в качестве переменных-членов при запуске фонового потока следующим образом:
mInput = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
mOutput = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
Для асинхронной связи ваш фоновый поток может выглядеть следующим образом:
@Override
public final void run() {
while (!Thread.currentThread().isInterrupted()) {
if (mInput == null) {
break;
}
String message = null;
try {
message = mInput.readLine();
} catch (IOException e) {
// handle the exception as you like
break;
}
if (Thread.currentThread().isInterrupted()) {
// thread was interrupted while reading
break;
} else if (message != null) {
// handle the message as you like
}
}
}
Используйте другой фоновый поток для отправки сообщений:
@Override
public void run() {
if (mOutput != null) {
mOutput.println(<message to be );
if (mOutput == null) {
// the above thread was interrupted while writing
} else if (!mOutput.checkError()) {
// everything went fine
} else {
// handle the exception
}
}
}
Кроме того, вам нужно будет закрыть потоки извне, чтобы readLine не блокировал навсегда:
try {
mOutput.close();
mInput.close();
mOutput = null;
mInput = null;
} catch (IOException e) {
// log the exception
}
Теперь, поскольку вы используете сокеты TCP, может случиться так, что сокет действительно мертв, а readLine все еще блокирует. Вы должны обнаружить это и закрыть потоки, как указано выше. Для этого вам придется добавить еще один поток (ну, хорошо), который периодически отправляет сообщения keep-alive. Если сообщение не было получено с удаленного устройства в течение X секунд, оно должно закрыть потоки.
Весь этот подход гарантирует, что розетка закрыта, и все потоки завершаются при любых обстоятельствах. Конечно, вы можете сделать сообщение синхронным, если это вам нужно, удалив поток-отправитель и включив println() внутри читательского потока. Надеюсь, это поможет вам (хотя ответ приходит на 8 месяцев позже).
Ответ 6
Попробуйте этот код -
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
Socket s = null;
String inMsg = null, msg2 = null;
try {
try {
s = new Socket(server, port);
} catch (Exception e) {
return;
}
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
try {
inMsg = in.readLine()
+ System.getProperty("line.separator");
} catch (Exception e) {
return;
}
out.write(message + "\n\r");
out.flush();
try {
msg2 = in.readLine();
if (msg2 == null) {
return;
}
} catch (Exception e) {
return;
}
out.close();
s.close();
} catch (Exception e) {
return;
}
}
}
};
Это работает для меня.
Ответ 7
Вы должны использовать Apache Commons IO: http://commons.apache.org/proper/commons-io/
См. IOUtils.copy()
http://commons.apache.org/proper/commons-io/javadocs/api-release/index.html?org/apache/commons/io/package-summary.html