Как вы можете принудительно очистить объект OutputStream, не закрывая его?

Мой вопрос основан на следующих предположениях, которые, я надеюсь, верны, потому что я верю им, когда читаю их, когда гуглю свои проблемы:

  1. Закрытие OutputStream сокета также закрывает сокет
  2. Метод flush() для OutputStream ничего не делает.

Поэтому мне в основном нужно каким-то образом сбросить данные из моего объекта OutputStream, чтобы мое приложение работало.

Если вас интересуют подробности, перейдите по следующим двум ссылкам:

. Странное поведение: отправка изображения с телефона Android на сервер Java (код работает)

Эта проблема была решена путем закрытия OutputStream. Это сбросило все данные на другой конец сокета и заставило мое приложение работать дальше, но это исправление вскоре привело к проблеме номер 2 — соответствующий сокет также закрывается:

. SocketException - "Сокет закрыт", даже если isConnected() возвращает true


person GrowinMan    schedule 15.04.2012    source источник
comment
Если вы голосуете против, имейте приличие объяснить, почему.   -  person GrowinMan    schedule 16.04.2012


Ответы (7)


Вы можете вызвать метод flush для OutputStream вместо close. Конкретные классы, унаследованные от OutputStream, будут переопределять flush(), чтобы делать что-то кроме ничего (запись данных в файл или отправка их по сети).

person smichak    schedule 15.04.2012
comment
Но если вы видите первый вопрос, который я разместил, изображение, переданное через сокет, не будет передано, пока OutputStream не будет закрыт. И использование flush() здесь не помогает. Что вы там предлагаете тогда? - person GrowinMan; 16.04.2012
comment
В ссылке, которую вы разместили, сервер ожидает EOF (конец файла) в потоке. EOF сигнализируется закрытием потока и TCP-соединения, поэтому сервер работает в цикле, пока вы не закроете выходной поток на стороне клиента. Если вы не хотите закрывать поток после передачи изображения, вы должны разработать другой метод сигнализации о завершении передачи изображения. Самый простой способ сделать это — отправить размер изображения в виде целого числа (4 байта) перед отправкой изображения. Затем сервер может прочитать до этого количества байтов, прежде чем выйти из цикла. - person smichak; 16.04.2012

Метод flush() класса OutputStream ничего не делает.

Это неправильно.

Это правда, что базовая реализация flush(), предоставляемая классом OutputStream, ничего не делает. Однако ваше приложение будет вызывать версию этого метода, предоставленную используемым вами классом фактического потока. Если класс потока не имеет семантики прямой записи, он переопределит flush(), чтобы сделать то, что требуется.

Короче говоря, если требуется сброс (а он требуется для выходного потока Socket), то вызов flush() будет правильным. (Если какой-то интернет-источник говорит вам об обратном, это либо неправильно, либо вы неправильно его интерпретируете.)


К вашему сведению, причина, по которой база OutputStream реализует flush() как неоперативную, заключается в следующем:

  • некоторым классам потока вывода не нужно ничего делать при сбросе; например ByteArrayOutputStream и
  • для потоковых классов, где flush() не является неоперативным, нет способа реализовать операцию на уровне базового класса.

Они могли (теоретически) спроектировать потоковые API так, чтобы OutputStream был абстрактным классом (и flush() абстрактным методом) или интерфейсом. Однако этот API был фактически заморожен до Java 1.0, и в то время не было достаточного опыта практического программирования на Java, чтобы понять, что дизайн API был неоптимальным.

person Stephen C    schedule 16.04.2012

Закрытие OutputStream сокета также закрывает сокет

Истинный.

Метод flush() для OutputStream ничего не делает.

Ложь. Есть переопределения. См. Javadoc для FilterOutputStream.flush(), BufferedOutputStream.flush(), ObjectOutputStream.flush() и многих других.

Таким образом, ваша первоначальная проблема не существует, поэтому вам не нужно «решение», которое вызывает проблему № 2.

person user207421    schedule 15.04.2012
comment
Но тогда что вы предлагаете для проблемы № 1? Первоначально я использовал flush() вместо close() для OutputStream, но тогда изображение не передавалось. - person GrowinMan; 16.04.2012
comment
Решение проблемы №1 — не закрывать поток. Если одноранговому узлу требуется конец потока, чтобы узнать, когда сообщение завершено, вы используете протокол приложения, который разрешает только одно сообщение, поэтому вам нужно изменить его. В TCP нет сообщений, только поток байтов. Сообщения остаются на ваше усмотрение. - person user207421; 23.11.2015

Вам действительно нужно смывать? У меня также была проблема, когда слушатель на сервере С# не мог получать данные, отправленные с Android (я пытался получить данные синхронно).

Я был уверен, что это связано с тем, что на стороне Android следующий код не сбрасывался.

OutputStream str = btSocket.getOutputStream();
str.write(data_byte);
// "This implementation does nothing"
str.flush();

Оказалось, что если я использую асинхронное получение данных на слушателе сервера - он получает данные, и никакой flush() на стороне клиента не требуется!

person infografnet    schedule 19.04.2013

Я попробую. У меня была такая же проблема. закрытие выходного потока - единственный способ «сбросить» данные. но так как мне все еще нужен поток вывода, это не вариант. поэтому сначала я отправляю длину массива байтов, out.writeInt, затем сам массив. когда все байты были прочитаны, т.е. buffer.length == in.readInt() я прерываю цикл

    ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    byte[] fileBytes;
    int n;
    int length;

    try
    {
        size = in.readInt();

        while((n = in.read(buffer)) != -1)
        {
            dataBuffer.write(buffer, 0, n);

            if(dataBuffer.toByteArray().length == length)
            {
                fileBytes = dataBuffer.toByteArray(); break;
            }
        }
    }
person everforth    schedule 24.08.2013

У меня такая же проблема. Добавьте «\n» в конце потока. сброс работает, но получатель не знает, закончилось ли сообщение

person user3248022    schedule 29.01.2014

YES на Android flush() ничего не делает (пример основан на API 23)

public Socket() {
    this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl();
    this.proxy = null;
}

public class PlainSocketImpl extends SocketImpl {

    @Override protected synchronized OutputStream getOutputStream() throws IOException {
        checkNotClosed();
        return new PlainSocketOutputStream(this);
    }


}

private static class PlainSocketOutputStream extends OutputStream {
        // doesn't override base class flush();
}

чтобы сбросить поток вывода без закрытия сокета, вы можете отключить вывод:

protected void shutdownOutput() throws IOException

-- это закроет файловый дескриптор WRITE.

вместо использования потока вывода вы можете писать напрямую в файловый дескриптор или создать собственную реализацию Socket с OutputStream, которая переопределит метод очистки (например, с помощью сокет Беркли реализация в c (через собственный вызов).

person ceph3us    schedule 20.06.2016