Класс UdpClient

121

Среда Microsoft .NET Framework предоставляет класс UdpClient для реализации в сети протокола UDP. Как и классы TcpClient и TcpListener, этот класс построен на классе Socket, но скрывает излишние члены, которые не требуются для реализации приложения, базирующегося на UDP.

Применять класс UdpClient довольно просто. Во-первых, создайте экземпляр UdpClient. Далее через вызов метода Connect() соединитесь с удаленным хостом. Эти два шага можно сделать в одной строке, если указать в конструкторе UdpClient удаленный IP-адрес и удаленный номер порта. Ранее было сказано, что протокол UDP не ориентирован на установление соединений, поэтому может возникнуть вопрос: так зачем же этот Connect? В действительности метод Connect() до отправки или получения данных не устанавливает соединение с удаленным хостом. Когда вы отправляете дейтаграмму, то пункт назначения должен быть известен, для этого нужно указать IP-адрес и номер порта.

Третий шаг состоит в отправке и получении данных с использованием метода Send() или Receive(). Наконец метод Close() закрывает соединение UDP.

Создание экземпляра класса UdpClient

Экземпляр класса UdpClient можно создать несколькими способами, которые отличаются передаваемыми параметрами. Наше использование объекта UdpClient зависит от того, как он создавался.

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

Можно также создать объект UdpClient, указав в параметре номер порта. В этом случае UdpClient будет слушать все локальные интерфейсы (т. е. использовать IP-адрес 0.0.0.0). Если номер порта находится вне пределов, указанных полями MinPort и MaxPort класса IPEndPoint, порождается исключение ArgumentOutOfRangeException (производное от ArgumentException). Если указанный порт уже занят, порождается исключение SocketException:

// Создаем UdpClient, используя номер порта
try
{
        UdpClient udpClient = new UdpClient(5001);
}
catch (ArgumentOutOfRangeException ex)
{
        Console.WriteLine("Некорректный номер порта");
}
catch (SocketException ex)
{
        Console.WriteLine("Порт уже используется");
}

Следующий способ заключается в использовании объекта IPEndPoint, представляющего локальный IP-адрес и номер порта, которые хотим выбрать для соединения. В этом случае первый шаг состоит в создании экземпляра класса IPEndPoint, а это можно сделать, использовав длинный IP-адрес или объект IPAddress. IP-адрес должен принадлежать одному из интерфейсов локальной машины, иначе будет порождено исключение SocketException с ошибкой "The requested address is not valid in it's context" (Запрошенный адрес в этом контексте неприменим).

Если конструктору передается пустой объект IPEndPoint, порождается исключение ArgumentNullException:

IPAddress ipAddr = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 5001);

// Создаем UdpClient, используя экземпляр IPEndPoint
try
{
       UdpClient udpClient = new UdpClient(ipEndPoint);
}
catch (ArgumentNullException ex)
{
       Console.WriteLine(ex.ToString());
}

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

Определение информации о соединении

После создания объекта UdpClient переходим ко второму шагу — подготовке информации о соединении, которая будет использоваться, когда потребуется отправить данные удаленному хосту. Вспомните, что протокол UDP не ориентирован на соединения и эта информация не нужна для получения данных, она используется, только чтобы указать, куда мы хотим отправить данные.

Эту информацию можно указать в любом из следующих трех мест: как мы уже видели, ее можно задать в конструкторе UdpClient, можно явно вызвать метод Connect() класса UdpClient или ее можно включить в метод Send() при фактической передаче данных.

Существуют три перегруженных метода Connect(): использующий объект IPEndPoint; устанавливающий соединение, используя IP-адрес и номер порта удаленного хоста; использующий имя DNS или машины и номер порта удаленного хоста.

Отправка данных через объект UdpClient

Получив экземпляр класса UdpClient и подготовив (необязательную) информацию о соединении, можно приступить к отправке данных. Неудивительно, что для этого вызывается метод Send(), который используется, чтобы послать дейтаграмму от клиента удаленному хосту. Важный момент, характеризующий протокол UDP, состоит в том, что после отправки данных удаленному хосту он не получает никаких подтверждений. Как и метод Connect(), метод Send() представлен несколькими перегруженными методами. Send() возвращает длину данных, которую можно использовать для проверки, правильно ли были отправлены данные.

Основная процедура отправки данных для класса UdpClient показана на следующей схеме:

Алгоритм отправки данных по UDP

Ниже показан полный пример отправки данных по UDP:

IPAddress ipAddr = IPAddress.Parse("127.0.0.1");

try
{
    // Создаем UdpClient
    UdpClient udpClient = new UdpClient();

    // Соединяемся с удаленным хостом
    udpClient.Connect(ipAddr, 5001);

    // Отправка простого сообщения
    byte[] bytes = Encoding.UTF8.GetBytes("Test");
    udpClient.Send(bytes, bytes.Length);

    // Закрываем соединение
    udpClient.Close();
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

Получение данных с использованием объекта UdpClient

Естественно, что для получения данных от удаленного хоста через UDP вызывается метод Receive(). Этот метод принимает один ссылочный параметр, экземпляр класса IPEndPoint, и возвращает в массиве байтов принятые данные. Обычно рекомендуется выполнять этот метод в отдельном потоке, поскольку он опрашивает базовый сокет на предмет поступления дейтаграмм и блокирует поток, пока данные не будут получены. Если он выполняется в основном потоке, выполнение программы приостанавливается, пока не будет получен пакет дейтаграммы.

Если объекту UdpClient уже указана информация о соединении в конструкторе или вызван метод Connect(), то метод Receive() будет принимать и возвращать нашему приложению только данные от указанной удаленной точки, а соединения от других источников будут отбрасываться. Если никакая информация о соединении не задавалась, будут приниматься все входящие соединения с локальной конечной точкой.

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

Все три шага показаны в следующем примере:

try
{
    // Создаем UdpClient
    UdpClient udpClient = new UdpClient(5001);

    // Создаем переменную IPEndPoint, чтобы передать ссылку на нее в Receive()
    IPEndPoint RemoteIPEndPoint = null;

    // Получение данных
    byte[] bytes = udpClient.Receive(ref RemoteIPEndPoint);
    string results = Encoding.UTF8.GetString(bytes);

    // Закрываем соединение
    udpClient.Close();
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

Методы групповой рассылки

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

Метод JoinMulticastGroup()

Этот метод позволяет присоединиться к группе. С этим методом объект UdpClient может получать групповые дейтаграммы, рассылаемые по указанному IP-адресу. Групповая рассылка позволяет доставлять данные по нескольким назначениям. Она характерна для протокола UDP и широко распространена в мире Интернета.

Метод JoinMulticastGroup() позволяет присоединиться к групповому IP-адресу. Существуют две перегруженные версии этого метода. Первая принимает только объект IPAddress, представляющий IP-адрес группы, к которой мы хотим присоединиться. Объект UdpClient будет получать любые дейтаграммы, рассылаемые по этому IP-адресу:

// Создаем UdpClient
UdpClient udpClient = new UdpClient();

// IP-адрес группы
IPAddress multicastIP = IPAddress.Parse("96.96.96.96");

try
{
     // Присоединяемся к группе
     udpClient.JoinMulticastGroup(multicastIP);
}
catch (Exception ex)
{
     Console.WriteLine(ex.ToString());
}

Второй перегруженный метод принимает групповой IP-адрес со значением TTL в поле int:

...
udpClient.JoinMulticastGroup(multicastIP, 30);

Метод DropMulticastGroup()

Этот метод можно использовать, чтобы отсоединить объект UdpClient от группы. Этот метод принимает один параметр — IP-адрес группы, которую этот клиент должен оставить:

// Отсоединяем группу
udpClient.DropMulticastGroup(multicastIP);
Пройди тесты
Лучший чат для C# программистов