Создание клиента 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



2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: