Список работ

Разработка сетевых игр

Курсовая работа

Иванова А. М., A-13-03, alibi_@mail.ru

Содержание

Введение

Для начала важно знать, какая архитектура будет использоваться для создания сетевой игры. Существуют две основные архитектуры – это клиент-клиент и клиент-сервер.
Архитектура клиент-клиент (также называемый peer-to-peer) достаточно проста (рис. 1 и 2), но её следует использовать только в играх для двоих.

Два клиента

Рис. 1. Схема архитектуры клиент-клиент с двумя клиентами

Число клиентов более двух

Рис. 2. Схема архитектуры клиент-клиент с числом клиентов более двух

Два клиента (компьютеры игроков) устанавливают соединение, используя IP-адреса или имена доменов (хостов). Но перед тем, как начать играть, нужно обменяться IP-адресами. Это небольшая, на первый взгляд, проблема может достаточно быстро вывести вас из себя. Некоторые меняются адресами по почте или в чате (с помощью IRC, ICQ, AIM, и т.д.). Другой вариант – это использование специального сервера, который помогает игрокам обменяться IP-адресами. Таким образом, игра будет приватная и только c двумя игроками. Возможно, это и хорошо, а может быть, и нет. В любом случае два компьютера обмениваются своими игровыми состояниями, и при этом оба производят полные вычисления игрового мира, тратя впустую вычислительные ресурсы.
При использовании архитектуры клиент-сервер на мощном компьютере, подключенном на хорошей скорости к сети, запускают специальное сервер-приложение. Теперь всё идёт через него. Сервер может производить вычисления для всех клиентов. А может и не производить. Это зависит только от нашего желания. Теоретически в игре может участвовать неограниченное число игроков. Зачастую серверы запускаются не на частных компьютерах, а на специальных машинах для многопользовательской игры, которые к тому же имеют доменные имена (например, www.mplayer.com ли www.heat.net) То есть теперь не нужно ни вводить IP-адреса, ни обмениваться ими. Также игры на основе технологии клиент-сервер имеют в своем составе так называемый лобби (lobby), где вы можете найти других игроков, подождать поединка или пообщаться с кем-нибудь.
На рис. 3 изображены 3 сокета.

3 сокета

Рис. 3. Схема архитектуры клиент-сервер

Один принадлежит клиенту, два остальных – серверу. В начале игры существует только один сокет, который прослушивается сервером (listen socket). Он прослушивает определённый порт (известный также клиенту) и при попытке соединения создаёт новый сокет для соединения с клиентом (connection socket). Это может показаться странным, но это вполне логично. Если использовать сокет, прослушиваемый сервером, для соединения и передачи данных, то никто не сможет присоединиться к игре, пока сокет занят.
На рис. 4 показано многопользовательское соединение при архитектуре клиент-сервер.

Многопользовательское соединение

Рис. 4. Многопользовательское соединение при архитектуре клиент-сервер

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

Постановка задачи

В работе решаются две следующие задачи:

На движок возлагаются следующие функции:

Проектирование

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

Сервер и клиенты

Рис. 5. Связь между клиентами устанавливается через сервер.
S – сервер;
C – клиент

Второй вариант – это "многослойный" сервер и клиенты (рис. 6).

Многослойный сервер

Рис. 6. Многослойный сервер и клиенты.
MS – главный сервер (master server);
SS – сервер 2-го слоя (slave server);
C – клиент

Сервер в этом случае состоит из одного главного сервера (master) и серверов "среднего слоя" (slave).
Первоначально клиент соединяется с MS, дальше MS решает, на какой сервер перекинуть клиента, естественно исходя из загруженности SS. Только MS имеет доступ к базе данных, SS получает всю необходимую информацию через MS.
Остановимся на первом варианте построения сети. Функционирование сервера иллюстрирует рис. 7, а клиента – рис. 8.

Функционирование сервера

Рис. 7. Сервер

Функционирование клиента

Рис. 8. Клиент

Анализ сообщений клиента выполняется по следующей схеме:

Реализация

Выбор протокола передачи данных выполнен из следующих альтернатив:

Выбор UDP обосновывается тем, он обеспечивает более высокую, чем TCP, скорость передачи данных, не гарантируя, однако, их доставку.
Среда разработки – Microsoft Visual C++ 2005, с поддержкой библиотек Qt4.3.3.
СУБД – Microsoft SQL Server 2005.

Интерфейс пользователя

При запуске приложения появляется окно выбора учетной записи (рис. 9).

Выбор учетной записи

Рис. 9. Выбор учетной записи

Также в дальнейшем планируется сделать регистрацию (на данном этапе она доступна только администратору БД).
Для соединения с сервером (Главное меню ->Settings ->Connect, рис. 10) нужно задать адрес и свободные слоты (не забудьте, однако, про файрвол).

Соединение с сервером

Рис. 10. Соединение с сервером

Далее представлено основное окно клиента (рис. 11).

Окно клиента

Рис. 11. Окно клиента

Слева список с активными игроками, выбрав одного из них и нажав на кнопку Game, устанавливается соединение. Модули с играми пока не активны, они будут добавлены в следующих версиях.
Кнопка Send обеспечивает отправку сообщения в чат или команды серверу (зависит от формата). При введении команды #help будет выведен полный список возможных команд, поддерживаемый данной версией клиента.

Заключение

Разработана клиент-серверная программа, поддерживающая:

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

Приложение

Протокол UDP

Протокол UDP (User Datagram Protocol, RFC-768) является одним из основных протоколов, расположенных непосредственно над IP. Он предоставляет прикладным процессам транспортные услуги, немногим отличающиеся от услуг протокола IP. Протокол UDP обеспечивает доставку дейтограмм, но не требует подтверждения их получения. Протокол UDP не требует соединения с удаленным модулем UDP ("бессвязный" протокол). К заголовку IP-пакета UDP добавляет поля "UDP-порт отправителя" и "UDP-порт получателя", которые обеспечивают мультиплексирование информации между различными прикладными процессами, а также поля "Длина UDP-сообщения" и "Контрольная сумма", позволяющие поддерживать целостность данных (рис. П.1).

Формат UDP-дейтограммы

Рис. П.1. Формат UDP-дейтограммы

Таким образом, если на уровне IP для определения места доставки пакета используется адрес, на уровне UDP – номер порта.

Протокол UDP не гарантирует доставки; по умолчанию предполагается, что вероятность потери пакета достаточно мала.

Прикладные процессы и модули UDP взаимодействуют через UDP-порты. Эти порты нумеруются начиная с нуля. Прикладной процесс, предоставляющий некоторые услуги (сервер), ожидает сообщений, направленных в порт, специально выделенный для этих услуг. Программа-сервер ждет, когда какая-нибудь программа-клиент запросит услугу.
Данные, отправляемые прикладным процессом через модуль UDP, достигают места назначения как единое целое. Например, если процесс-отправитель производит 5 записей в порт, то процесс-получатель должен будет 5 раз выполнить операцию чтения. Размер каждого записанного сообщения будет совпадать с размером каждого прочитанного. Протокол UDP сохраняет границы сообщений, определяемые прикладным процессом. Он никогда не объединяет несколько сообщений в одно и не делит одно сообщение на части
Длина сообщения равна числу байт в UDP-дейтограмме, включая заголовок. Поле "Контрольная сумма" содержит код, полученный в результате контрольного суммирования UDP-заголовка и поля "Данные". Не трудно видеть, что этот протокол использует заголовок минимального размера (8 байт). Таблица номеров UDP-портов можно посмотреть в RFC-1700. Номера портов от 0 до 255 стандартизованы и использовать их в прикладных задачах не рекомендуется. Но и в интервале 255-1023 многие номера портов заняты, поэтому прежде чем использовать какой-то порт, следует заглянуть в RFC-1700.
Зарегистрировано ряд портов для стандартного применения и в диапазоне 1024-65535. Например:

Номер портаОбозначениеНазначение
1397Аudio-activemailАктивная звуковая почта
1398Video -activemailАктивная видео почта
5002RFEРадио-Ethernet
6000 – 6063X11Система X Window
7008AFS3-updateСервер-сервер актуализация

Модуль IP передает поступающий IP-пакет модулю UDP, если в заголовке этого пакета указан код протокола UDP. Когда модуль UDP получает дейтограмму от модуля IP, он проверяет контрольную сумму, содержащуюся в ее заголовке. Если контрольная сумма равна нулю, это означает, что отправитель ее не подсчитал. ICMP, IGMP, UDP и TCP протоколы имеют один и тот же алгоритм вычисления контрольной суммы (RFC-1071). Но вычисление контрольной суммы для UDP имеет некоторые особенности. Во-первых, длина UDP-дейтограммы может содержать нечетное число байт, в этом случае к ней добавляется нулевой байт, который служит лишь для унификации алгоритма и никуда не пересылается. Во-вторых, при расчете контрольной суммы для UDP и TCP добавляются 12-байтные псевдозаголовки, содержащие IP-адреса отправителя и получателя. Код протокола и длина дейтограммы приведены на рис. П.2.

Псевдозаголовок, для расчета контрольной суммы

Рис. П.2. Псевдозаголовок, используемый при расчете контрольной суммы

Как и в случае IP-дейтограммы, если вычисленная контрольная сумма равна нулю, в соответствующее поле будет записан код 65535.
Если контрольная сумма правильная (или равна 0), то проверяется порт назначения, указанный в заголовке дейтограммы. Если прикладной процесс подключен к этому порту, то прикладное сообщение, содержащееся в дейтограмме, становится в очередь к прикладному процессу для прочтения. В остальных случаях дейтограмма отбрасывается. Если дейтограммы поступают быстрее, чем их успевает обрабатывать прикладной процесс, то при переполнении очереди сообщений поступающие дейтограммы отбрасываются модулем UDP. Следует учитывать, что во многих посылках контрольное суммирование не охватывает адреса отправителя и места назначения. При некоторых схемах маршрутизации это приводит к зацикливанию пакетов в случае повреждения его адресной части (адресат не признает его "своим").
Так как максимальная длина IP-дейтограммы равна 65535 байтам, максимальная протяженность информационного поля UDP-дейтограммы составляет 65507 байт. На практике большинство систем работает с UDP-дейтограммами с длиной 8192 байта или менее. Детальное описание форматов, полей пакетов и пр. можно найти в RFC-768.

Протокол TCP

Протокол TCP (Transmission Control Protocol, RFC-793, -1323, -1644[T/TCP], -2018, -2581, -2582[RENO], -2861, -2873, -2883[SACK], -2923[MTU], -2988[RTO], -3293[GSMP], -3448[TFRC], -3465, -3481) в отличии от UDP осуществляет доставку дейтограмм, называемых сегментами, в виде байтовых потоков с установлением соединения. Протокол TCP применяется в тех случаях, когда требуется гарантированная доставка сообщений. Он использует контрольные суммы пакетов для проверки их целостности и освобождает прикладные процессы от необходимости таймаутов и повторных передач для обеспечения надежности. Для отслеживания подтверждения доставки в TCP реализуется алгоритм "скользящего" окна. Наиболее типичными прикладными процессами, использующими TCP, являются FTP (File Transfer Protocol - протокол передачи файлов) и telnet. Кроме того, TCP используют системы SMTP, HTTP, X-window, RCP (Remote Copy), а также "r"-команды. Внутренняя структура модуля TCP гораздо сложнее структуры UDP. Подобно UDP прикладные процессы взаимодействуют с модулем TCP через порты. Под байтовыми потоками здесь подразумевается то, что один примитив, например, read или write может вызвать посылку адресату последовательности сегментов, которые образуют некоторый блок данных (сообщение). Использование портов открывает возможность осуществлять несколько соединений между двумя сетевыми объектами (работать с разными процессами).
Примером прикладного процесса, использующего TCP, может служить FTP, при этом будет работать стек протоколов ftp/tcp/ip/ethernet. Хотя протоколы UDP и TCP могли бы для сходных задач использовать разные номера портов, обычно этого не происходит. Модули TCP и UDP выполняют функции мультиплексоров/демультиплексоров между прикладными процессами и IP-модулем. При поступлении пакета в модуль IP он будет передан в TCP- или UDP-модуль согласно коду, записанному в поле протокола данного IP-пакета. Формат сегмента (пакета) TCP представлен на рис. П.3.

Формат TCP-сегмента

Рис. П.3. Формат TCP-сегмента

Если IP-протокол работает с адресами, то TCP, также как и UDP, с портами. Именно с номеров портов отправителя и получателя начинается заголовок TCP-сегмента. 32-битовое поле "Код позиции в сообщении" определяет порядковый номер первого октета в поле данных пользователя. В приложениях передатчика и приемника этому полю соответствуют 32-разрядные счетчики числа байт, которые при переполнении обнуляются. При значении флага SYN=1 в этом поле лежит код ISN (Initial Sequence Number; смотри ниже описание процедуры установления связи), выбираемый для конкретного соединения. Первому байту, передаваемому через созданное соединение, присваивается номер ISN+1. Значение ISN может задаваться случайным образом. Но в UNIX 4.4BSD при загрузке ОС ISN делается равным 1 (это нарушает требования RFC), а далее увеличивается на 640000 каждые полсекунды. Аналогичная инкрементация осуществляется при установлении нового соединения. В RFC рекомендуется увеличивать счетчик ISN на 1 каждые 4 микросекунды.
32-битовое поле "Номер октета, который должен прийти следующим" содержит код, который на единицу больше номера последнего успешно доставленного (принятого) байта. Содержимое этого поля интерпретируется получателем сегмента, только если присутствует флаг ACK. В заголовках всех сегментов, передаваемых после установления соединения это поле заполняется, а флаг AСK=1.
В ТСР предусмотрен режим полнодуплексной передачи. При этом данные могут передаваться в обоих направлениях независимо. В ходе обмена каждая из сторон должна отслеживать позиционные номера передаваемых и принимаемых байт. Если получен сегмент с некоторым кодом поля "Номер октета, который должен прийти следующим", это означает, что все октеты с номерами меньше указанного в данном поле, доставлены благополучно. Если благополучно доставлены байты с номерами 0-N, а затем получен сегмент с номерами байтов (N+k) - (N+k+m), такой сегмент будет буферизован, но подтверждения его получения не последует. Вместо этого посылается отклик, с кодом "Номер октета, который должен прийти следующим"=(N+1). В случае получения сегмента с неверной контрольной суммой будет послан отклик, идентичный предыдущему. Дублированные отклики позволяют детектировать потерю пакета.
Поле HLEN - определяет длину заголовка сегмента, которая измеряется в 32-разрядных словах. Это поле нужно, так как в заголовке могут содержаться поля опций переменной длины. Далее следует поле "Резерв", предназначенное для будущего использования, в настоящее время должно обнуляться. Поле "Размер окна" сообщает, сколько октетов готов принять получатель (флаг ACK=1) вслед за байтом, указанным в поле "Номер октета, который должен прийти следующим". Окно имеет принципиальное значение, оно определяет число сегментов, которые могут быть посланы без получения подтверждения. Значение ширины окна может варьироваться во время сессии (смотри описание процедуры "медленного старта"). Значение этого поля равное нулю также допустимо и указывает, что байты вплоть до указанного в поле "Номер октета, который должен прийти следующим", получены, но адресат временно не может принимать данные. Разрешение на посылку новой информации может быть дано с помощью посылки сегмента с тем же значением поля "Номер октета, который должен прийти следующим", но ненулевым значением поля "Размер окна". Поле "Контрольная сумма" предназначено для обеспечения целостности сообщения. Контрольное суммирование производится по модулю 1. Перед контрольным суммированием к TCP-сегменту добавляется псевдозаголовок, как и в случае протокола UDP, который включает в себя адреса отправителя и получателя, код протокола и длину сегмента, исключая псевдозаголовок. Поле "Указатель важной информации" представляет собой указатель последнего байта, содержащий информацию, которая требует немедленного реагирования. Поле имеет смысл лишь при флаге URG=1, отмечающем сегмент с первым байтом "важной информации". Значение разрядов в 6-битовом коде "Флаги" (интерпретируются слева направо) описано в следующей таблице:

Обозначение битов поля "Флаги" Значение бита, если он равен 1
URGФлаг важной информации, поле "Указатель важной информации" имеет смысл, если URG=1
ACKНомер октета, который должен прийти следующим, правилен
PSHЭтот сегмент требует выполнения операции push. Получатель должен передать эти данные прикладной программе, как можно быстрее
RSTПрерывание связи
SYNФлаг для синхронизации номеров сегментов, используется при установлении связи
FINОтправитель закончил посылку байтов

Если флаг ACK=0, значение поля "Номер октета, который должен прийти следующим", игнорируется. Флаг URG=1 устанавливается в случае нажатия пользователем клавиш Del или Ctrl-С.
Поле "Опции" зарезервировано на будущее и в заголовке может отсутствовать, его размер переменен и дополняется до кратного 32-бит с помощью поля "Заполнитель". Формат поля "Опции" представлен на рис. П.4.

Формат опций для TCP-сегментов

Рис. П.4. Формат опций для TCP-сегментов

В настоящее время определены следующие опции:

  1. Конец списка опций.
  2. Никаких операций. Используется для заполнения поля опции до числа октетов, кратного 4.
  3. Максимальный размер сегмента (MSS).

В поле "Вид" записывается код опции, поле LEN содержит число октетов в описании опции, включая поля "Вид" и LEN. Определены также опции со значением Вид=4,5,6,7. В предложении T/TCP (RFC-1644) описаны опции 11, 12 и 13. Поле "Данные" может иметь переменную длину, верхняя его граница задается значением MSS (Maximum Segment Size). Значение MSS может быть задано при установлении соединения каждой из сторон независимо. Для Ethernet MSS=1452 байта.
Поле "Данные" в TCP-сегменте может и отсутствовать, характер и формат передаваемой информации задается исключительно прикладной программой, теоретически максимальный размер этого поля составляет в отсутствии опций 65495 байт. TCP является протоколом, который ориентируется на согласованную работу ЭВМ и программного обеспечения партнеров, участвующих в обмене информацией. Установление связи клиент-сервер осуществляется в три этапа:

  1. Клиент посылает SYN-сегмент с указанием номера порта сервера, который предлагается использовать для организации канала связи (active open).
  2. Сервер откликается, посылая свой SYN-сегмент, содержащий идентификатор ISN. Начальное значение ISN не равно нулю. Процедура называется passive open.
  3. Клиент отправляет подтверждение получения SYN-сегмента от сервера с идентификатором равным ISN (сервера)+1.

Стандартная процедура установления связи представлена на рис. П.5 (под словом "стандартная" подразумевается отсутствие каких-либо отклонений от штатного режима, например, одновременного инициирования соединения со стороны сервера и клиента).

Процедура установления связи между клиентом и сервером

Рис. П.5. Процедура установления связи (представлены состояния клиента и сервера)

Если же соединение одновременно инициируется и клиентом и сервером, то в конечном итоге будет создан только один виртуальный канал.
Префикс S на рисунке указывает на сервер, а С - на клиента. Параметры в скобках обозначают относительные значения ISN. После установления соединения ISN(S) = s_seq_1, а ISN(C) = c_seq_1.
Каждое соединение должно иметь свой неповторимый код ISN. Для реализации режима соединения прикладная программа на одном конце канала устанавливается в режим пассивного доступа (passive open), а операционная система на другом конце ставится в режим активного доступа (active open). Протокол TCP предполагает реализацию 11 состояний (established, closed, listen, syn_sent, syn_received и т.д.; см. также RFC-793), переход между которыми строго регламентирован. Машина состояний для протокола TCP может быть описана диаграммой, представленной на рис. П.6.

Машина состояний для протокола TCP

Рис. П.6. Машина состояний для протокола TCP

Здесь состояние closed является начальной и конечной точкой последовательности переходов. Каждое соединение стартует из состояния closed. Из диаграммы машины состояний видно, что ни одному из состояний не поставлен в соответствие какой-либо таймер. Это означает, что машина состояний TCP может оставаться в любом из состояний сколь угодно долго. Исключение составляет keep-alive таймер, но его работа является опционной, а время по умолчанию составляет 2 часа. Это означает, что машина состояния может оставаться 2 часа без движения. В случае, когда две ЭВМ (C и S) попытаются установить связь друг с другом одновременно, реализуется режим simultaneous connection (RFC-793). Обе ЭВМ посылают друг другу сигналы SYN. При поучении этого сигнала партнеры посылают отклики SYN+ACK. Обе ЭВМ должны обнаружить, что SYN и SYN+ACK относятся к одному и тому же соединению. Когда C и S обнаружат, что SYN+ACK соответствует посланному ранее SYN, они выключат таймер установления соединения и перейдут непосредственно в состояние syn_recvd.
Существует проблема при пересылке данных по каналам TCP, которая называется синдром узкого окна. Такого рода проблема возникает в том случае, когда данные поступают отправителю крупными блоками, а интерактивное приложение адресата считывает информацию побайтно. Предположим, что в исходный момент времени буфер адресата полон и передающая сторона знает об этом (window = 0). Интерактивное приложение считывает очередной октет из TCP-потока, при этом TCP-агент адресата посылает уведомление отправителю, разрешающее ему послать один байт. Этот байт будет послан и снова заполнит до краев буфер получателя, что вызовет отправку ACK со значением window = 0. Этот процесс может продолжаться сколь угодно долго, понижая коэффициент использования канала ниже паровозного уровня.
Кларк предложил не посылать уведомление о ненулевом значении ширины окна при считывании одного байта, а лишь после освобождения достаточно большого пространства в буфере. Например, когда адресат готов принять MSS байтов или когда буфер наполовину пуст.
Предполагается, что получатель пакета практически всегда посылает отправителю пакет-отклик. Отправитель может послать очередной пакет, не дожидаясь получения подтверждения для предшествующего. Таким образом, может быть послано k пакетов, прежде чем будет получен отклик на первый пакет (протокол "скользящего окна").

В протоколе TCP "скользящее окно" используется для регулировки трафика и препятствия переполнения буферов.

Идея скользящего окна отображена на рис. П.7.

Скользящее окно

Рис. П.7. Схема использования скользящего окна

Здесь предполагается, что ширина окна равна 7 (k=7; это число может меняться в очень широких пределах).
После прихода отклика на пакет 1 окно смещается вправо на одну позицию. Теперь отправитель может послать и пакет 8. Если порядок прихода откликов нарушается, сдвиг окна может задержаться. Размер окна в сегментах определяется соотношением:

window > RTT * B / MSS,

где B – полоса пропускания канала в бит/с, а MSS – максимальный размер сегмента в битах.
Для протокола TCP механизм скользящего окна может работать на уровне сокетов или сегментов. В первом случае нужно учитывать каждый раз размер поля данных переданного и подтвержденного сегмента. В TCP-протоколе используется три указателя.
Первый указатель определяет положение левого края окна, отделяя посланный сегмент, получивший подтверждение, от посланного сегмента, получение которого не подтверждено. Второй указатель отмечает правый край окна и указывает на сегмент, который может быть послан до получения очередного подтверждения. Третий указатель помечает границу внутри скользящего окна между уже посланными сегментами и теми, которые еще предстоит послать. Получатель организует аналогичные окна для обеспечения контроля потока данных. Если указатель 3 совпадет с указателем 2, отправитель должен прервать дальнейшее отправление пакетов до получения хотя бы одного подтверждения. Обычно получатель посылает одно подтверждение (ACK) на два полученных сегмента.
Регулирование трафика в TCP подразумевает существование двух независимых процессов: контроль доставки, управляемый получателем с помощью параметра window, и контроль перегрузки, управляемый отправителем с помощью окна перегрузки cwnd (congestion window) и ssthreth (slow start threshold). Первый процесс отслеживает заполнение входного буфера получателя, второй - регистрирует перегрузку канала, а также связанные с этим потери и понижает уровень трафика. В исходный момент времени при установлении соединения cwnd делается равным одному MSS, а ssthreth=65535 байтам. Программа, управляющая пересылкой, никогда не пошлет больше байт, чем это задано cwnd и объявленным получателем значением window. Когда получение очередного блока данных подтверждено, значение cwnd увеличивается. Характер этого увеличения зависит от того, осуществляется медленный старт или реализуется процедура подавления перегрузки. Если cwnd меньше или равно ssthreth, выполняется медленный старт, в противном случае осуществляется подавление перегрузки. В последнем случае cwndi+1 = cwndi + MSS/8 +(MSS*MSS)/cwnd. Если возникает состояние перегрузки канала значение cwnd снова делается равным одному MSS.

Окно перегрузки (cwnd) позволяет согласовать полную загрузку виртуального соединения и текущие возможности канала, минимизируя потери пакетов при перегрузке.

Помимо окон перегрузки и получателя в TCP используется третий параметр - порог (иногда он называется порогом медленного старта ssthresh, slow start threshold, обычно измеряется в байтах). При установлении соединения ssthresh=64 Kбайт. В случае возникновения таймаута значение ssthresh делается равным CWND/2, а само значение CWND приравнивается MSS. Далее запускается процедура медленного старта, чтобы выяснить возможности канала. При этом экспоненциальный рост cwnd осуществляется вплоть до значения ssthresh. Когда этот уровень cwnd достигнут, дальнейший рост происходит линейно с приращением на каждом шагу равным MSS (рис. П.8).

Ширина окна при медленном старте

Рис. П.8. Эволюция ширины окна при медленном старте

Здесь предполагается, что MSS=1 Кбайт. Началу диаграммы соответствует установка значения ssthresh=16 Кбайт. Данная схема позволяет более точно выбрать значение cwnd. После таймаута, который на рисунке произошел при передаче c номером 12, значение порога понижается до 12 Кбайт (=cwnd/2). Ширина окна cwnd снова начинает расти от передачи к передаче, начиная с одного сегмента, вплоть до нового значения порога ssthresh=12 Кбайт. Стратегия с экспоненциальным и линейным участками изменения ширины окна переполнения позволяет несколько приблизить среднее его значение к оптимальному. Для локальных сетей, где значение RTT невелико, а вероятность потери пакета мала, оптимизация задания cwnd не так существенна, как в случае протяженных внешних (например, спутниковых) каналов. Ситуация может поменяться, если в локальной сети имеется фрагмент, где вероятность потерь пакетов велика. Таким фрагментом может быть МАС-бридж (или переключатель), один из каналов которого подключен к сегменту Fast Ethernet, а другой к обычному Ethernet на 10 Мбит/c. Если такой мост не снабжен системой подавления перегрузки (до сих пор такие приборы не имели подобных систем), то каждый из пакетов будет потерян в среднем 9 раз, прежде чем будет передан (здесь предполагается, что передача идет из сегмента FE). При этом cwnd будет практически все время равно MSS, что крайне неэффективно при передаче по каналам Интернет. Такие потери вызовут определенные ошибки при вычислении среднего значения и дисперсии RTT, а как следствие и величин таймаутов. Применение в таких местах маршрутизаторов или других приборов, способных реагировать на перегрузку посредством ICMP(4), решает эту проблему.
Для взаимного согласования операций в рамках TCP-протокола используется четыре таймера:

  1. Таймер повторных передач (retransmission; RTO) контролирует время прихода подтверждений (ACK). Таймер запускается в момент посылки сегмента. При получении отклика ACK до истечения времени таймера, он сбрасывается. Если же время таймера истекает до прихода ACK, сегмент посылается адресату повторно, а таймер перезапускается.
  2. Таймер запросов (persist timer), контролирующий размер окна даже в случае, когда приемное окно закрыто. При window = 0 получатель при изменении ситуации посылает сегмент с ненулевым значением ширины окна, что позволит отправителю возобновить свою работу. Но если этот пакет будет потерян, возникнет тупик, тогда каждая из сторон ждет сигнала от партнера. Именно в этой ситуации и используется таймер запросов. По истечении времени этого таймера отправитель пошлет сегмент адресату. Отклик на этот сегмент будет содержать новое значение ширины окна. Таймер запускается каждый раз, когда получен сегмент с window = 0.
  3. Таймер контроля работоспособности (keep alive), который регистрирует факты выхода из строя или перезагрузки ЭВМ-партнеров. Время по умолчанию равно 2 часам. Keep alive-таймер не является частью TCP-спецификации. Таймер полезен для выявления состояний сервера half-open при условии, что клиент отключился (например, пользователь выключил свою персональную ЭВМ, не выполнив LOGOUT). По истечении времени таймера клиенту посылается сегмент проверки состояния. Если в течение 75 с. будет получен отклик, сервер повторяет запрос 10 раз с периодом 75 с., после чего соединение разрывается. При получении любого сегмента от клиента таймер сбрасывается и запускается вновь.
  4. 2MSL-таймер (Maximum Segment Lifetime) контролирует время пребывания канала в состоянии TIME_WAIT. Выдержка таймера по умолчанию равно 2 мин (FIN_WAIT-таймер). Таймер запускается при выполнении процедуры active close в момент посылки последнего ACK.

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

RTTm = a * RTTm + (1 - a) * RTTi,

где RTTi - результат очередного измерения, RTTm - величина, полученная в результате усреднения предыдущих измерений, а – коэффициент сглаживания, обычно равный 0.9. RFC-793 рекомендует устанавливать время таймаута для ретрансмиссии (повторной передачи), значение RTO - Retransmission Timeout равно RTO = RTTm * b, где b равно 2. От корректного выбора этих параметров зависит эффективная работа каналов. Так, занижение времени ретрансмиссии приводит к неоправданным повторным посылкам сегментов, перегружая каналы связи. Для более точного выбора RTO необходимо знать дисперсию RTT. Несколько более корректную оценку RTO можно получить из следующих соотношений (предложено Джекобсоном в 1988 году, он же позднее предложил целочисленный алгоритм реализации этих вычислений):

RTTm = RTTm + g * (RTTi - RTTm)
D = D + d * (|RTTi - RTTm| - D)
RTO = RTTm + 4D,

где D – среднее отклонение RTT от равновесного значения, а коэффициенты g = 0,125, d = 0.25. Чем больше g, тем быстрее растет RTO по отношению к RTT. Это хорошо работает до тех пор, пока не произойдет таймаут и ретрансмиссия. В этом случае, получив ACK, трудно решить, какому сегменту соответствует это подтверждение, первому или второму. На эту проблему впервые обратил внимание Фил Карн. Решением проблемы является приостановка коррекции RTTm при таймауте и ретрансмиссиях. Значение RTO зависит от пропускной способности канала и от специфических задержек, например в случае спутниковых каналов. В основном RTO лежит в секундном диапазоне (5-15 с.). Наиболее вероятная причина потери пакетов - это перегрузка канала на участках между отправителем и приемником. Указанием на то, что пакет потерян, может служить таймаут или получение дубликата сегмента ACK. Если произошел таймаут, система переходит в режим "медленного старта" (ширина окна перегрузки делается равной 1 сегменту, а значение порога медленного старта - ssthresh делается равным двум сегментам). При инициализации канала переменная ssthresh обычно равна 65535. Дублирование ACK индицирует потерю пакета до наступления таймаута. В этом случае сначала меняется алгоритм приращения величины окна перегрузки cwnd (замедляется темп его роста). После прихода очередного ACK новое значение cwnd вычисляется по формуле:

cwndi+1 = cwndi + (размер_сегмента * размер_сегмента) / cwndi + размер_сегмента / 8

Если же в этот момент величина окна перегрузки меньше или равна некоторому порогу (ssthresh), осуществляется "медленный старт". Следует помнить, что TCP требует посылки немедленного подтверждения (дублированного ACK) при обнаружении прихода сегментов с нарушением порядка следования. Причиной нарушения порядка следования может быть флуктуация задержки в сети или потеря пакета. Если получено три или более задублированных ACK, это является убедительным указанием на потерю пакета и, не дожидаясь таймаута, осуществляется его повторная передача. Перехода в режим медленного старта в этом случае не производится, но понижаются значения cwnd и ssthresh (почти вдвое).
Когда TCP-канал закрывается и за время сессии переслано более 16 полых окон, а адресат достижим не через маршрут по умолчанию, то в таблицу маршрутизации заносится следующая информация: усредненное значение RTT, значение дисперсии RTT и ssthresh.
Если в ходе TCP-сессии получено сообщение ICMP(4) (переполнение канала – quench), требующее снижения потока данных, то cwdn делается равным одному сегменту, а величина порога медленного старта ssthresh не изменяется. На ICMP-сообщения о недостижимости сети или ЭВМ программы TCP-уровня не реагируют вообще.
Нулевой размер окна блокирует посылку информации и этим система время от времени пользуется. Что произойдет, если получатель послал сегмент, объявляющий окно ненулевым, а подтверждение получения этого сегмента не прошло? TCP-протокол не предусматривает посылки ACK на само подтверждение. Адресат ждет в этом случае данных, так как он уже объявил о существовании ненулевого окна с помощью соответствующего ACK, а отправитель ждет этого недошедшего ACK, чтобы начать передачу данных. Для разрешения этой тупиковой ситуации используется таймер запросов, который периодически посылает зондирующие сегменты получателю. Цель этого зондирования - выяснение существования окна ненулевой ширины. Таймер запросов запускается при получении информации об обнулении ширины окна приемником. Если за определенное время не поступает сегмента, сообщающего об изменении размера окна, таймер начинает посылать зондирующие сегменты. Таймер запросов использует базовую временную шкалу с периодом в 500 мс, а период посылки зондирующих сегментов лежит в диапазоне 5-60 с. Такой сегмент содержит только один байт данных. Таймер запросов не прерывает своей работы до тех пор, пока не будет подтверждено открытие окна или пока прикладная задача не завершит свою работу, выключив канал связи.
Будучи однажды создан, канал TCP может существовать "вечно". Если клиент и сервер пассивны, они не заметят того, например, что какой-то бульдозер оборвал кабель или спутник связи покоится на дне океана. Чтобы это обнаружить, либо клиент либо сервер должны попытаться послать какую-то информацию. Чтобы информировать систему об этих и подобных им жизненных неурядицах, предусмотрен таймер контроля работоспособности (keep alive). Многим читателям, возможно, приходилось легкомысленно выключать питание своего персонального компьютера, не позаботившись о корректном logout из процедуры telnet или FTP. Если бы не существовало этого таймера, вы бы, включив ЭВМ, обнаружили, что "находитесь" в заморском депозитарии, где были вчера. Но таймер контроля работоспособности может и прервать сессию, если какой-то промежуточный маршрутизатор произвел перезагрузку или был вынужден поменять маршрут. Принцип работы таймера работоспособности предельно прост. Если канал пассивен, например, 2 часа, сервер посылает клиенту сегмент-зонд. При этом ЭВМ-клиент может быть в одном из следующих четырех состояний:

Временная постоянная таймера keep alive является системной переменной единой для всех пользователей ЭВМ или даже локальной сети.
Расширение пропускной способности и надежности телекоммуникационных каналов делает актуальной совершенствование протоколов.

Источники

  1. www.gamedev.ru
  2. www.trolltech.com
  3. www.book.itep.ru
  4. Mulholland A. Programming multiplayer games. 2004, Wordware Publishing, Inc.
  5. Armitage G., Claypool M., Branch P. Networking and Online Games: Understanding and Engineering Multiplayer Internet Games. 2006, Wiley.

Список работ

Рейтинг@Mail.ru