Подтвердить что ты не робот

Не удается прочитать сокет 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 вместо прямых потоков - никакого эффекта.

4b9b3361

Ответ 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;
            }
        }

    }

};

Это работает для меня.