Более быстрое обнаружение сломанного сокета в Java / Android - PullRequest
10 голосов
/ 12 июня 2011

Фон

Мое приложение собирает данные с телефона и отправляет их на удаленный сервер.
Данные сначала сохраняются в памяти (или в файле, когда он достаточно большой) и каждые X секунд или около тогоприложение сбрасывает эти данные и отправляет их на сервер.

Очень важно, чтобы каждый отдельный фрагмент данных отправлялся успешно, я бы предпочел отправлять данные дважды, а не вообще.

Проблема

В качестве теста я настроил приложение на отправку данных с отметкой времени каждые 5 секунд, это означает, что каждые 5 секунд на сервере появляется новая строка.
Если я убью сервер, я ожидаю, чтоСтроки для остановки, теперь они должны быть записаны в память.

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

Проблема, однако, заключается в том, что когда япри остановке сервера требуется около 20 секунд для начала операций ввода-вывода, что означает, что в течение этих 20 секунд приложение успешно отправляет события и удаляет их из памяти.но они никогда не доходят до сервера и теряются навсегда.

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

Возможно, это один из более простых вопросов TCP, но нетем не менее, я не нашел никакого решения.

Материал, который я пробовал

  • Настройка Socket.setTcpNoDelay(true)
  • Удалениевсе буферизованные писатели и просто OutputStream напрямую
  • Очистка потока после каждой отправки

Дополнительная информация

Я не могу изменить способсервер отвечает, что означает, что я не могу сказать серверу подтвердить данные (это больше, чем механизм TCP), сервер просто будет молча принимать данные, не отправляя ничего обратно.

Фрагмент кода

Инициализация класса:

socket = new Socket(host, port);
socket.setTcpNoDelay(true);

Куда отправляются данные:

while(!dataList.isEmpty()) {
    String data = dataList.removeFirst();
    inMemoryCount -= data.length();
    try {
        OutputStream os = socket.getOutputStream();
        os.write(data.getBytes());
        os.flush();
    }
    catch(IOException e) {
        inMemoryCount += data.length();
        dataList.addFirst(data);
        socket = null;
        return false;
    }
}

return true;

Обновление 1

Я скажу это снова, я не могу изменить поведение сервера.
Он получает данные по TCP иd UPD и не отправляет никаких данных обратно, чтобы подтвердить получение.Это факт, и в идеальном мире сервер подтвердит данные, но этого просто не произойдет.


Обновление 2

Решение, опубликованное Fraggle, работает идеально (закрытиесокет и ожидание закрытия входного потока).

Это, однако, связано с новым набором проблем.
Поскольку я разговариваю по телефону, я должен предположить, что пользователь не может отправить бесконечное количествобайтов, и я хотел бы, если возможно, сократить весь трафик данных.

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

Сама строка не такая длинная (около 30 символов), но она складывается, еслиЯ закрываю и открываю сокет слишком часто.

Одно из решений состоит в том, чтобы «сбрасывать» данные каждые X байтов, проблема в том, что я должен выбирать X мудро;если он слишком большой, будет отправлено слишком много повторяющихся данных, если сокет выйдет из строя, а если он слишком мал, слишком большие издержки.


Окончательное обновление

Мое окончательное решение - "очистите сокет, закрыв его каждые X байтов, и если все не выздоровело, эти байты X будут отправлены снова.

Возможно, это создаст несколько дублирующих событий на сервере, но их можно отфильтровать там.

Ответы [ 6 ]

3 голосов
/ 12 июня 2011

Решение Дана - это то, что я бы предложил сразу после прочтения вашего вопроса, он получил мой положительный голос.

Теперь я могу предложить работать вокруг проблемы? Я не знаю, возможно ли это с вашей установкой, но один из способов иметь дело с плохо спроектированным программным обеспечением (это ваш сервер, извините) - это обернуть его, или в fancy-design-pattern-talk предоставить фасад, или в Откровенный разговор поставил прокси-сервер перед вашим сервером боли. Разработайте полезный протокол, основанный на подтверждении, обеспечьте, чтобы прокси-сервер сохранял достаточное количество образцов данных в памяти, чтобы иметь возможность обнаруживать и терпеть разрыв соединений, и т. Д. И т. Д. Короче говоря, приложение для телефона должно подключаться к прокси-серверу, находящемуся где-то на серверном уровне машина использует «хороший» протокол, затем подключите прокси к процессу сервера, используя «плохой» протокол. Клиент отвечает за генерацию данных. Прокси-сервер отвечает за работу с сервером.

Просто еще одна идея.

Редактировать 0:

Вам может показаться, что это интересно: Последняя страница SO_LINGER или: почему мой tcp ненадежен .

3 голосов
/ 12 июня 2011

Плохие новости: Вы не можете обнаружить сбойное соединение , кроме как пытаясь отправить или получить данные по этому соединению.

Хорошая новость: как вы говорите, все нормально, если вы отправляете дубликаты данных. Таким образом, ваше решение не беспокоиться об обнаружении отказа менее чем за 20 секунд, которые сейчас требуются. Вместо этого просто сохраняйте кольцевой буфер, содержащий данные за последние 30 или 60 секунд. Каждый раз, когда вы обнаруживаете ошибку и затем повторно подключаетесь, вы можете начать сеанс, повторно отправив сохраненные данные.

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

2 голосов
/ 12 июня 2011

См. Принятый ответ здесь: Java-сокеты и удаленные соединения

  1. socket.shutdownOutput ();
  2. ожидание возврата inputStream.read ()-1, указывая, что узел также отключил свой сокет
0 голосов
/ 12 июня 2011

Используйте http POST вместо сокетного соединения, тогда вы можете отправить ответ на каждый пост.На стороне клиента вы удаляете данные из памяти только в том случае, если ответ указывает на успешность.

Конечно, это больше затрат, но дает вам то, что вы хотите, 100% времени.

0 голосов
/ 12 июня 2011

Как насчет отправки дейтаграмм UDP по отдельному сокету UDP, когда удаленный хост отвечает на каждый, а затем, когда удаленный хост не отвечает, вы разрываете соединение TCP?Он быстро обнаруживает разрыв ссылки:)

0 голосов
/ 12 июня 2011

Не будет работать: сервер не может быть изменен

Не может ли ваш сервер подтвердить каждое сообщение, полученное с другим пакетом?Клиент не удалит сообщения, которые сервер еще не подтвердил.

Это повлияет на производительность.Чтобы избежать замедления, вы можете продолжать отправлять сообщения до получения подтверждения и подтверждать несколько сообщений в одном ответном сообщении.

Если вы отправляете сообщение каждые 5 секунд, а сетевой стек не обнаруживает отключения в течение 30 секунд, вам нужно будет сохранить только 6 сообщений.Если 6 отправленных сообщений не подтверждены, вы можете считать, что соединение не работает.(Я предполагаю, что логика переподключения и отправки невыполненных заданий уже реализована в вашем приложении.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...