Разработка для реальных сетей

В идеальном мире, объединяясь в сеть “просто работает”. Ваше сетевое соединение надежно, быстро, и низкая задержка. В реальном мире, объединяя работы в сеть большую часть времени, но когда это повреждается, это часто прерывает странные и захватывающие пути. Например:

Несмотря на то, что Ваше программное обеспечение не может волшебно фиксировать действительно поврежденную сеть, плохо записанный сетевой код может легко сделать вещи очень, намного хуже. Например, предположите, что сервер в большой степени перегружается и занимает 45 секунд для ответа на каждый запрос. Если Ваше программное обеспечение соединяется с тем сервером с 30 вторыми тайм-аутами, это способствует рабочей нагрузке сервера, но никогда успешно получает любые данные.

И даже когда сеть работает отлично, плохо записанный сетевой код может вызвать проблемы для пользователя — плохое время работы от батареи, низкая производительность, и т.д. Разделы в этой главе описывают вещи, которые Ваше программное обеспечение должно сделать для минимизации боли пользователей, и когда условия идеальны и когда вещи идут не так, как надо.

Используя питание и пропускную способность эффективно

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

Сетевая операция стоит пользовательского времени потому что:

Сетевая операция также стоит пользовательских денег, потому что пропускная способность не свободна. Некоторые затраты включают:

Как разработчик сетевого программного обеспечения, именно Ваша ответственность минимизировать питание и пропускную способность Ваше программное обеспечение использует.

Обработайте свои передачи в пакетном режиме, и неактивный каждый раз, когда возможно

При записи кода в целом, до максимальной возможной степени, необходимо выполнить столько работы, сколько Вы можете и затем возвращаться к состоянию ожидания. Это применяется вдвойне для сетевой активности. Например:

  • Если Ваши потоковые видеоклипы приложения от сервера HTTP, загрузите весь файл сразу (или по крайней мере значительная часть того файла) вместо того, чтобы запросить его маленькая часть за один раз.

  • Если Ваши рекламные объявления подач приложения, загрузите несколько объявлений сразу и покажите им в течение времени, вместо того, чтобы загрузить их, поскольку они необходимы.

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

Загрузка содержания понемногу вызывает две проблемы. Во-первых, это делает приложение более чувствительным к незначительным сетевым задержкам, вызывая остановы, видео заикание, и т.д. Во-вторых, это сохраняет сотовую связь или радио Wi-Fi включенными почти постоянно. Когда Ваше приложение связывается по сотовому соединению, это тратит впустую питание, особенно. Если Ваше приложение вместо этого загружает много данных в течение краткого промежутка времени и затем позволяет беспроводному соединению идти полностью неактивное, можно значительно улучшить время работы от батареи пользователей.

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

Загрузите самый маленький возможный ресурс, и ресурсы кэша локально

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

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

  • Передача данных использует питание путем хранения сетевых аппаратных средств и CPU включенными в течение более длительных промежутков времени. Путем уменьшения размера активов передачи программы могут улучшить время работы от батареи пользователей (предполагающий, что это приводит к сетевому уменьшению в общих переданных данных, в среднем).

  • Если Ваши пользователи находятся на измеренном Интернет-соединении (таком как сотовый телефон), передавание меньших активов может также сократить вексели Ваших пользователей.

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

Много высокоуровневых APIs в OS X и iOS (NSURL, например), предоставляют поддержку для кэширования (NSURLCache, например). Однако необходимо выбрать надлежащие размеры для кэшей. Используете ли Вы встроенное кэширование API или создаете Ваше собственное, необходимо экспериментировать с размерами кэша и заменяющими политиками определить то, что имеет большую часть смысла для приложения.

Решение сетевых проблем корректно

В сегодняшнем очень мобильном мире Вы больше не можете предполагать, что Интернет-соединение, когда-то установленное, останется установленным, или что пропускная способность никогда не будет увеличиваться или уменьшаться — как это сказано, изменение является единственной константой. Как разработчик, необходимо запланировать эти общие отказы и разработать код для обработки их соответственно.

Проект для переменной доступности сетевого интерфейса

Доступность сетевого интерфейса может регулярно изменяться по бесчисленным причинам, особенно в iOS. Например, пользователь мог:

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

  • Переместитесь вне диапазона текущей сети Wi-Fi.

  • Активируйте Авиарежим или выключите Wi-Fi.

  • Отключите сетевой кабель.

Из-за этого, при записи программного обеспечения, использующего сеть, Вы должны быть подготовлены к отказам сети. Когда сетевая ошибка происходит, Ваша программа должна решить, что сделать на основе многих соображений — самое главное, был ли запрос выполнен явно пользователем или нет.

  • Для запросов, выполненных по воле пользователя:

    • Всегда попытка сделать соединение. Не пытайтесь предположить, доступна ли сетевая служба, и не кэшируйте то определение.

    • Если связь прерывается, используйте SCNetworkReachability API, чтобы помочь диагностировать причину отказа. Тогда:

      • Если соединение, отказавшее из-за случайной ошибки, попытайтесь делать соединение снова.

      • Если связь прервалась, потому что узел недостижим, ожидайте SCNetworkReachability API для вызова зарегистрированного обратного вызова. Когда узел становится достижимым снова, Ваше приложение должно повторить попытку подключения автоматически без вмешательства пользователя (если пользователь не принял некоторые меры для отмены запроса, такого как закрытие окна браузера или щелчок по кнопке отмены).

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

  • Для запросов, выполненных в фоновом режиме:

    • Попытка сделать соединение.

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

    • Если связь прерывается, используйте SCNetworkReachability API для ожидания узла для становления достижимыми снова затем повторите запрос, если все еще полезно сделать так.

    • Не выводите на экран диалоговое окно; пользователи обычно не заботятся об отказах в фоновых загрузках, что они не инициировали.

    • Избегите повторять слишком быстро, даже когда сетевая достижимость, APIs говорит Вашему приложению, что изменилась сеть. Когда связи неоднократно прерываются, необходимо постепенно увеличивать количество времени, Вы ожидаете между попытками, пока Вы не достигаете довольно длинного интервала повторной попытки (15 минут, например).

    Ваша программа должна быть в состоянии корректно реагировать на изменения в текущем сетевом интерфейсе. Для поддержки этого используйте SCNetworkReachability API. Когда доступные сетевые интерфейсы изменяются, путем регистрации для уведомлений изменения сети предупреждена программа.

    Когда текущий сетевой интерфейс изменяется, пример кода Достижимости демонстрирует регистрацию обратного вызова для уведомления. Считайте Ссылку SCNetworkReachability для полного обсуждения SCNetworkReachability API.

Сгенерированы ли запросы пользователями и фон), SCNetworkReachability API обеспечивает хороший способ наблюдать за интерфейсными изменениями доступности, которые могут потребовать, чтобы Вы повторно соединили существующие соединения. Когда сетевой интерфейс, который Вы используете, уходит, необходимо быстро повторно соединиться для предотвращения ненужных задержек пользователя.

Кроме того, на iOS, если Вы соединяетесь по сотовому соединению, необходимо быстро повторно соединиться в фоновом режиме каждый раз, когда служба Wi-Fi становится доступной снова. Соединения Wi-Fi используют меньше заряда батареи, обычно быстрее, и часто стоят пользователю меньшего количества денег, чем сотовые соединения.

Проект для переменной скорости сети

Ваша программа должна быть подготовлена к скорости сети измениться, даже когда текущий сетевой интерфейс остается неизменным. Например, когда пользователь мобильного устройства изменяет местоположения, производительность на сетях Wi-Fi или сотовых сетях может измениться значительно, или из-за увеличенной интерференции или потому что устройство было передано к более занятой ретрансляционной станции. Это не вносит большое изменение в расположении также; даже обход от одной комнаты до другого может вызвать существенные изменения и в Wi-Fi и в скоростях услуги сотовой связи.

Далее, даже игнорируя конкуренцию и интерференцию, сам интерфейс ничего не говорит Вам о фактической пропускной способности, доступной несколько транзитных участков далеко. Сеть Wi-Fi могла бы быть быстрой, когда пользователь пытается соединиться с Google, но маршрут между пользователем и Вашим сервером мог проходить через сотовый модем или спутниковый восходящий грузовик. Точно так же у пользователя могло бы быть соединение гигабитного Ethernet с серверами в локальной сети, но только 128-килобитное восходящее соединение с внешним миром. Поэтому Вы не должны делать предположения о скорости сети на основе текущего сетевого интерфейса.

Существует только один способ определить скорость сети: используйте его. После загрузки мелкой суммы данных можно установить первоначальную смету скорости сети. Необходимо продолжать контролировать уровень загрузки, чтобы поддержать точную оценку, и затем скорректировать ожидания соответственно. Например, если Вы - потоковое видео, и Вы устанавливаете тот свой курс потоковой передачи, больше не не отстает от воспроизведения, Вы могли бы переключиться тихо на более низкий поток пропускной способности на лету и продолжать воспроизведение, как будто ничто не произошло. Если Вы позже решаете, что скорости загрузки улучшились, можно переключиться назад столь же тихо.

Проект для высокой задержки

Как разработчик, предположите, что Ваши пользователи могли бы использовать соединение высокой задержки. Высокая задержка особенно распространена на некоторых типах интерфейсов сотовой сети из-за ограниченного количества временных интервалов, которые могут использоваться данным устройством. Например, задержка туда и обратно по Торцовому соединению часто измеряется в секундах. Однако даже полувторая задержка, вызванная спутниковой связью или умеренно занятым соединением DSL, может вызвать серьезные проблемы, если Вы не планируете ее в Вашем проектировании программного обеспечения.

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

Для предотвращения этой проблемы, каждый раз, когда программа должна отправить многократные сообщения (запросы ресурса, подтверждения, и т.д.), которые не зависят от друг друга, отправьте их всех одновременно вместо того, чтобы ожидать ответа на одно сообщение прежде, чем отправить следующее. Рисунок 1-1 иллюстрирует ускорение, которое Ваша программа получает от отправки многократных сообщений одновременно.

  Сравнение рисунка 1-1 времени отклика для одновременных и последовательных запросов

Если Вы используете NSURLConnection в Вашем приложении для iOS можно легко получить ускорение путем включения конвейерной обработки HTTP. Когда конвейерная обработка включена, Ваше соединение автоматически отправляет многократные Запросы HTTP одновременно. Позвольте конвейерно обработать путем вызова setHTTPShouldUsePipelining: метод на NSMutableURLRequest объект Вы обеспечиваете для своего соединения.

Тест при различных условиях

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

Вот несколько вещей протестировать:

  • Удостоверьтесь, что Ваше программное обеспечение остается применимым даже с паршивой пропускной способностью. Настройте свое потребление пропускной способности так, как Вы можете.

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

  • Когда сетевое соединение отбрасывает пакеты, Ваше программное обеспечение должно продолжать функционировать, просто более медленно.

Можно также счесть полезным использовать сторонние инструменты, такие как tcptrace для визуализации образцов доступа к сети программного обеспечения под оскорбительным состоянием сети.