Создание клиента UDP
Проблема
Вы хотите обмениваться сообщениями с другим процессом, используя UDP (датаграммы).
Решение
Чтобы создать манипулятор для сокета UDP, воспользуйтесь либо низкоуровневым модулем
Socket для уже существующего манипулятора:
use Socket;
socket(SockHandle, PF_INET, SOCK_DGRAM, getprotobyname("udp"))
or die "socket: $!";
либо модулем IO::Socket, возвращающим анонимный манипулятор:
use IO::Socket;
$handle = IO::Socket::INET->new(Proto => 'udp')
or die "socket: $@"; # Да, здесь используется $@
Отправка сообщения на компьютер с именем $HOSTNAME и адресом порта $PORTNO выполняется так:
$ipaddr = inet_aton($HOSTNAME);
$portaddr = sockaddr_in($PORTNO, $ipaddr);
send(SockHandle, $MSG, 0, $portaddr) == length($MSG)
or die "cannot send to $HOSTNAME($PORTNO): $!";
Получение сообщения, длина которого не превышает $MAXLEN:
$portaddr = recv(SockHandle, $MSG, $MAXLEN, 0) or die "recv: $!";
($portno, $ipaddr) = sockaddr_in($portaddr);
$host = gethostbyaddr($ipaddr, AF_INET);
print "$host($portno) said $MSG\n";
Комментарий
Датаграммныё сокеты не похожи на потоковые. Поток создает иллюзию постоянного соединения.
Он напоминает телефонный звонок — установка связи обходится дорого, но в дальнейшем связь
надежна и проста в использовании. Датаграммы больше похожи на почту — если ваш знакомый
находится на другом конец света, дешевле и проще отправить ему письмо, чем дозвониться по
телефону. Датаграммы потребляют меньше системных ресурсов, чем потоки. Вы пересылаете
небольшой объем информации, по одному сообщению за раз. Однако доставка сообщений не
гарантируется, и они могут быть приняты в неверном порядке. Если очередь получателя переполнится,
как маленький почтовый ящик, то дальнейшие сообщения теряются.
Если датаграммы настолько ненадежны, зачем же ими пользоваться? Просто некоторые приложения
наиболее логично реализуются с применением датаграмм Например, при пересылке аудиоданных
важнее сохранить поток в целом, чем гарантировать прохождение каждого пакета, особенно
если потеря пакетов вызвана недостаточной пропускной способностью. Датаграммы также часто
применяются в широковещательной рассылке (аналог массовой рассылки рекламных объявлений
по почте). В частности, широковещательные пакеты используются для отправки в локальную
подсеть сообщений типа: «Есть здесь кто-нибудь, кто хочет быть моим сервером?»
Поскольку датаграммы не создают иллюзии постоянного соединения, в работе с ними вы
располагаете несколько большей свободной. Вам не придется вызывать connect для подключения
сокета к удаленной точке, с которой вы обмениваетесь данными. Вместо этого каждая датаграмма
адресуется отдельно при вызове send. Предполагая, что $remote_addr является результатом
вызова sockaddr_in, поступите следующим образом:
send(MYSOCKET, $msg_buffer, $flags, $remote_addr)
or die "Can't send: $!\n";
Единственный часто используемый флаг, MSG_OOB, позволяет отправлять и принимать
внеполосные (out-of-band) данные в нетривиальных приложениях.
Удаленный адрес ($remote_addr) должен представлять собой комбинацию порта и адреса
Интернета, возвращаемую функцией sockaddr_in модуля Socket. Если хотите, вызовите
connect
для этого адреса — в этом случае последний аргумент при вызове send можно опускать,
а все сообщения будут отправлены этому получателю. В отличие от потоковых коммуникаций,
один датаграммный сокет позволяет подключиться к другому компьютеру.
В примере приведена небольшая программа, использующая протокол UDP. Она устанавливает
связь с портом времени UDP на компьютере, имя которого задается в командной строке, или по
умолчанию на локальном компьютере. Программа работает не на всех компьютерах, но при наличии
сервера UDP вы получите 4-байтовое целое число, байты которого упакованы в сетевом порядке;
число равно количеству секунд с 1900 года по данным этого компьютера. Чтобы передать это
время функции преобразования localtime или gmtime, необходимо вычесть из него количество
секунд от 1900 до 1970 года.
#!/usr/bin/perl
# clockdrift - сравнение текущего времени с другой системой
use strict;
use Socket;
my ($host, $him, $src, $port, $ipaddr, $ptime, $delta);
my $SECS_of_70_YEARS = 2_208_988_800;
socket(MsgBox, PF_INET, SOCK_DGRAM, getprotobyname("udp"))
or die "socket: $!";
$him = sockaddr_in(scalar(getservbyname("time", "udp")),
inet_aton(shift || '127.1'));
defined(send(MsgBox, 0, 0, $him))
or die "send: $!";
defined($src = recv(MsgBox, $ptime, 4, 0))
or die "recv: $!";
($port, $ipaddr) = sockaddr_in($src);
$host = gethostbyaddr($ipaddr, AF_INET);
my $delta = (unpack("N", $ptime) - $SECS_of_70_YEARS) - time();
print "Clock on $host is $delta seconds ahead of this one.\n";
Если компьютер, с которым вы пытаетесь связаться, не работает или ответ утерян,
программа застрянет при вызове recv в ожидании ответа, который никогда не придет.
См. такжеОписание функций
send,
recv и
unpack
Proverte kod v komentariyah gde pro list tam oshibki detskie
Оставить комментарий:
|
|