Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

UDP

01.01.2007

7. UDP

7.1. Обзор

UDP (User Datagram Protocol) используется для датаграмм (datagram) и без установки соединения. UDP позволяет посылать короткие пакеты на узел, без установки соединения с ним. Для UDP пакетов не гарантируется доставка и не гарантируется тот же порядок приема, в каком они были посланы. При посылке UDP пакета, он посылается в одном блоке. Поэтому вы не должны превышать максимальный размер пакета, указанный в вашем TCP/IP стеке.

Из-за этих факторов, многие люди считают UDP малопригодными. Но это не совсем так. Многие потоковые протоколы, такие как Real Audio, используют UDP.

Примечание: термин "потоковый" может быть легко перепутан с термином поток (stream) для TCP. Когда вы видите эти термины, вы должны разобраться с контекстом, в котором они применяются, для определения точного значения.

7.2. Надежность

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

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

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

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

7.3. Широкополосносные сообщения (Broadcast)

UDP имеет уникальную возможность, что его делает очень применимым. Это возможность широкополосной посылки. Широкополосность (Broadcast) означает, что может быть послано одно сообщение, по получено многими получателями. Это не то же самое, как многоадресность (multicasting). Многоадресность – это модель подписки. Когда получатели делают подписку на получение и они добавляются в список рассылки. С помощью широкополосной рассылки, сообщение посылается по сети и все кто его слушают, могут принять его, без необходимости подписываться.

Многоадресные сообщения подобны газетной рассылки. Только те люди, которые подписались на доставку, получают его. Широкополосные сообщения аналогичны радиовещанию (От переводчика: в английском языке это одно и тоже слово broadcasting). Любой, кто имеет радиоприемник, может настроить его на любую радиостанцию и принимать ее. Радиослушатель не обязан оповещать радиостанцию, что он хочет слушать.

Конкретный IP адрес для сообщения, можно рассчитать, основываясь на IP отправителя и маски сети. Есть также особый случай, это адрес 255.255.255.255, который так далеко, насколько это возможно.

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

7.4. Размеры пакетов

Большинство операционных систем позволяют иметь размер UDP пакета 32K или даже 64K. Но, как правило, маршрутизаторы имеют более серьезные ограничения. UDP не должны превышать допустимый размер, разрешенный маршрутизатором или другим сетевым устройством. Но нет никакой возможности узнать это ограничение.

Поэтому рекомендуется, чтобы UDP пакеты были длиной в 8192 байт или менее, при передаче за пределами локальной сети.  Но в некоторых случаях и этот размер велик. Для абсолютной гарантии делайте ваши пакеты в 1024 байт или менее.

7.5. Схемы подтверждений

7.5.1. Обзор

В локальной сети надежность UDP достаточно высокая. Но в случае WAN или Интернет вы, возможно, пожелаете  разработать какую ни будь схему подтверждений.

7.5.2. Схема с подтверждениями

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

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

7.5.3. Схема с последовательностями

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

Такое поведение при потерянных пакетов можно услышать при передачи реальной речи при передачи по сотовому телефону. В некоторых случаях вы слышите пропуски или заикания.

7.6. Компонент TIdUDPClient

TIdUDPClient – это базовый компонент для посылки UDP пакетов. Наиболее часто используемый метод – это Send, в котором используются свойства Host и Port для посылки UDP пакета. Аргументом функции является строка.

Есть также метод SendBuffer, который выполняет туже задачу, но аргументами являются Buffer и Size.

TIdUDPClient также может использоваться как сервер, для приема входящих UDP пакетов.

7.7. Компонент TIdUDPServer

Поскольку UDP работает без соединений, то TIdUDPServer немного иначе, чем TIdTCPServer. TIdUDPServer не имеет режимов подобным TIdSimpleServer, Так как UDP не использует соединений, то TIdUDPClient (видимо здесь опечатка и имеется в виду TIdUDPServer) имеет только простые слушающие методы.

При активации TIdUDPServer создает слушающий поток для входящих UDP пакетов. Для каждого принятого UDP пакета, TIdUDPServer возбуждает событие OnUDPRead в главном кодовом потоке, или в контексте слушающего потока, в зависимости от свойства ThreadedEvent.

Когда свойство ThreadedEvent сброшено, то событие OnUDPRead возбуждается в контексте главного кодового потока. Когда свойство ThreadedEvent установлено, то событие OnUDPRead возбуждается в контексте слушающего потока.

Вне зависимость от состояния ThreadedEvent true или false, блокируется обработка других сообщений. Поэтому обработка OnUDPRead по возможности должна быть короткой.

7.8. UDP пример - RBSOD

7.8.1. Обзор

В данной главе приводится пример UDP клиента и UDP сервера. Данный пример это реальное приложение, которое может быть использовано для  шуток в корпоративное среде. Тем не менее, он должен использоваться с осторожностью.

Пример называется «Remote BSOD invocator» - или сокращено RBSOD. RBSOD может быть использован для создания ложных BSOD (От переводчика: Blue Screen Of Dead – это знаменитый экран смерти) на машинах коллег (или врагов). BSOD не является реальным BSOD, и не делает ни каких потерь данных у жертвы. Тем не менее, он  должен напугать его startle. Сам BSOD также может быть сброшен удаленно.

Примечание: по современной антивирусной терминологии, подобная программа считается вирусом-  Joke Virus и очень опасно, поскольку с этим часто борются методом переустановки системы с нуля. Кроме того, это заготовка для более опасного трояна.

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

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

 

Предоставлены следующие параметры.

7.8.1.1. IP Address

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

Если оставить это поле пустым, то сообщение будет послано в широковещательном режиме по всей подсети (или докуда оно сможет дойти) и на всех компьютерах, на которых установлен сервер возникнет BSOD.

7.8.1.2. Поле Message

Поле Message задает текст сообщения на машине жертвы, которое будет указано в BSOD screen. Значение по умолчанию следующее:

A fatal exception OE has occurred at 0028:C0011E36 in VXD VMM(01)00010E36. The current application will be terminated. (Произошла фатальная ошибка OE по адресу 0028:C0011E36 в VXD VMM(01)00010E36. Текущее приложение будет закрыто).

Данный текст взят из реального BSOD и будет казаться реальным для пользователя.

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

Windows crashed again. I am the Blue Screen of Death. No one hears your screams. (Виндоус снова упал. Я синий экран смерти. Никто не услышит ваших криков).

Three things are certain: Death, taxes, and lost data. Guess which has occurred. (Три вещи неотвратимы: смерть, налоги и потерянные данные. Отгадайте, какая случилась).

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

Сообщения находятся в файле messages.dat и вы можете добавить свои.

7.8.1.3. Use a Custom Message

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

Ugly brown suit error. I cannot continue in such company. (Очень неприятная ошибка в одежде. Я больше не могу оставаться далее в такой компании).

7.8.1.4. Show Any Key

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

Этот параметр должен отвратить вашего босса или Бейсик программиста потратить часы на поиск клавиши. Данный параметр наиболее хорошо использовать до отправки в аэропорт для длительного путешествия или когда вы хотите занять его надолго. (А себя в длительных поисках новой работы)

7.8.1.5. Show Trademark

Если данный параметр используется, то в нижнем углу экрана будет мелкая, но читаемая  надпись: * The BSOD is a trademark of the Microsoft Corporation.

7.8.1.6. Клавиша Show

Нажатие клавиши Show приведет к генерации BSOD на удаленном компьютере.

7.8.1.7. Клавиша Clear

Нажатие клавиши Clear используется для удаленного снятия «BSOD». Обычно вы отдаете это на откуп пользователю, но в некоторых случая вы, возможно, захотите сами его снять. Ну, например, перед походом к пользователю.

7.8.2. Сервер RBSOD

Но сначала посмотрим на сервер.

7.8.2.1. Установка

Сервер назван svchost (еще одна любимая вещь у вирусов) вместо RBSODServer или другим более говорящим именем. Это сделано, чтобы для более простой установки на другом компьютере и одновременно скрываете его. Имя svchost – это нормальное имя системного исполнимого файла, который запускает множество копий исполнимых файлов. Если вы посмотрите в диспетчере задач, то вы несколько запущенных копий подобного процесса. Поскольку вы разместите свой специальную копию в другой папке, а не в системной, то он не будет драться с родным, но будет выглядеть как нормальный в диспетчере задач.

Сервер не имеет окон и не появляется в панели задач и системном трее (там, где часики). Если вы хотите его остановить, то запустите диспетчер задач и выберите тот svchost, который запущен от имени пользователя. Системный svchosts запущен от SYSTEM (это не совсем так). После выбора вашей «версии» нажмите клавишу [Снять задачу].

Для инсталляции просто скопируйте сервер на машину клиента (compile without packages for easiest deployment) и запустит его. Он останется в памяти пока пользователь не перезагрузит свой компьютере. После перезагрузки программа не будет перезапущена. Если вы желаете, что бы программа автоматически стартовала при каждой перезагрузки, то просто добавьте ее в автозагрузку или в реестр, для невидимого запуска.

7.8.2.2. Исходный код

Сервер состоит из двух частей, Server.pas и BSOD.pas. BSOD.pas содержит форму, которая используется для показа BSOD экрана. BSOD.pas не содержит Indy кода и поэтому не рассматривается здесь.

Server.pas – это главная форма приложения и содержит один UDP сервер. Свойство port установлено в 6001 и свойство active установлено в True. Когда приложение запускается, то оно немедленно начинает слушать порт для входящих UDP пакетов.

Как ранее обсуждалось UDP подобен пейджеру. Поскольку ему не требуется соединение для приема данных. Пакеты данных просто появляются как один кусок. Для каждого принятого UDP пакета, UDP сервер возбуждает событие OnUDPRead. Больше никаких других событий не требуется для реализации UDP сервера. Когда возбуждается событие OnUDPRead, полный пакет уже принят и готов к использованию.

Событию OnUDPRead передаются три аргумента:

1.ASender: TObject – это компонент, который возбудил данное событие. Это применимо только если будет создано несколько UDPServers серверов и используют один и тот же метод для события. Это очень редко нужно.
2.AData: TStream – это основной аргумент и он содержит сам пакет. UDP пакеты могут содержать текст и/или двоичные данные. Поэтому Indy предоставляет их в виде потока. Для доступа к данным, просто используйте методы read класса TStream.
3.ABinding: TIdSocketHandle – этот аргумент пригоден для получения расширенной информации. Это востребовано, если используется связывание (bindings). Вот пример как выглядит событие OnUDPRead в RBSOD сервере:
procedure TformMain.IdUDPServer1UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
var
  LMsg: string;
begin
  if AData.Size = 0 then 
  begin
    formBSOD.Hide;
  end 
  else 
  begin
    // Move from stream into a string
    SetLength(LMsg, AData.Size);
    AData.ReadBuffer(LMsg[1], Length(LMsg));
    //
    formBSOD.ShowBSOD(Copy(LMsg, 3, MaxInt), 
Copy(LMsg, 1, 1) = 'T', 
Copy(LMsg, 2, 1) = 'T');
  end;
end;

Обратим внимание, что оператор if проверяет на нулевую длину. Это вполне легально посылать и принимать пустые UDP пакеты. В данном случае это используется для сигнализации серверу о снятии BSOD.

Если размер не равен 0, то данные копируются в локальную строковую перемену с помощью TStream.ReadBuffer.

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

7.8.3. Клиент RBSOD

RBSOD клиент еще проще чем сервер. RBSOD клиент состоит из одной формы: Main.pas. Main.pas содержит несколько событий, но большинство из них относятся к пользовательскому интерфейсу и понятны сами по себе.

Основной Indy код в клиенте RBSOD,  который находится в обработчике the OnClick клавиши Show.

IdUDPClient1.Host := editHost.Text;
IdUDPClient1.Send(
iif(chckShowAnyKey.Checked, 'T', 'F')
+ iif(chckTrademark.Checked, 'T', 'F')
+ s);
 

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

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

Аргумент, переданный для пересылки, немедленно отсылается как UDP пакет и поэтому все данные для пересылки должны быть посланы с помощью одного пакета.

Indy также содержит перегруженный метод SendBuffer для посылки данных из буферов.

В случае RBSOD просто содержит два символа, которые определяют, как показывать торговую марку и показывать any key, затем актуально сообщение для отображения.

Еще есть другой Indy код в клиенте RBSOD, который находится в обработчике события OnClick для клавиши Clear и он почти идентичен приведенному ранее отрывку.