Передача данных через TCP
Проблема
Требуется передать или принять данные по TCP-соединению.
Решение
Следующее решение предполагает, что связь осуществляется через Интернет.
Первый вариант — print или <>:
print SERVER "What is your name?\n";
chomp ($response = <SERVER>);
Второй вариант — функции send и
recv:
defined (send(SERVER, $data_to_send, $flags))
or die "Can't send : $!\n";
recv(SERVER, $data_read, $maxlen, $flags)
or die "Can't receive: $!\n";
Третий вариант — соответствующие методы объекта IO::Socket:
IO::Socket;
$server->send($data_to_send, $flags)
or die "Can't send: $!\n";
$server->recv($data_read, $flags)
or die "Can't recv: $!\n";
Чтобы узнать, могут ли быть получены или приняты данные, воспользуйтесь функцией
select,
для которой в классе IO::Socket также предусмотрена удобная оболочка:
use IO::Select;
$select = IO::Select->new();
$select->add(*FROM_SERVER);
$select->add($to_client);
@read_from = $select->can_read($timeout);
foreach $socket (@read_from) {
# Прочитать ожидающие данные из $socket
}
Комментарий
Сокеты используются в двух принципиально различных типах ввода/вывода, каждый из которых
обладает своими достоинствами и недостатками. Стандартные функции ввода/вывода Perl,
используемые для файлов (кроме seek и sysseek), работают и для потоковых сокетов, однако
для датаграммных сокетов необходимы системные функции send и recv, работающие с целыми записями.
При программировании сокетов очень важно помнить о буферизации. Хотя буферизация и была
спроектирована для повышения быстродействия, она может повлиять на интерактивное поведение
некоторых программ. Если при вводе данных с помощью <> будет обнаружен разделитель записей,
программа может попытаться прочитать из сокета больше данных, чем доступно в данный момент.
И print и <> используют буферы stdio, поэтому без включения автоматической очистки буфера для
манипулятора сокета данные не отправятся на другой конец в момент их передачи функцией print.
Вместо этого они будут ждать заполнения буфера.
Вероятно, для клиентов и серверов с построчным обменом данных это подходит — при условии,
что вы не забыли включить автоматическую очистку буфера. Новые версии IO::Socket делают это
автоматически для анонимных файловых манипуляторов, возвращаемых IO::Socket->new.
Но стандартный ввод/вывод — не единственный источник буферизации. Операции вывода
(print, printf, syswrite — или send для сокета TCP) буферизуются на уровне операционной
системы по так называемому алгоритму Нейгла. Если пакет данных отправлен, но еще не подтвержден,
другие передаваемые данные ставятся в очередь и отправляются либо после набора следующего
полного пакета либо при получении подтверждения. В некоторых ситуациях (события мыши, оконных
системах, нажатия клавиш в приложениях реального времени) такая буферизация оказывается
неудобной или попросту неверной. Буферизация Нейгла отключается параметром сокета TCP_NODELAY:
use Socket;
require "sys/socket.ph": # Для &TCP_NODELAY
setsockopt(SOCKET, SOL_SOCKET, &TCP_NODELAY, 1)
or die "Couldn't disable Nagle's algorithm: $!\n";
Ее повторное включение происходит так:
setsockopt(SOCKET, SOL_SOCKET, &TCP_NODELAY, 0)
or die "Couldn't enable Nagle's algorithm: $!\n";
Как правило, TCP_NODELAY все же лучше не указывать. Буферизация TCP существует не зря,
поэтому не отключайте ее без крайней необходимости — например, если ваше приложение работает
в режиме реального времени с крайне интенсивным обменом пакетов.
TCP_NODELAY загружается из sys/socket.ph — этот файл не устанавливается автоматически
вместе с Perl, но может быть легко построен.
Буферизация чрезвычайно важна, поэтому в вашем распоряжении имеется функция
select.
Она определяет, какие манипуляторы содержат непрочитанный ввод, в какие манипуляторы возможна
запись и для каких имеются необработанные «исключительные состояния». Функция select получает
три строки, интерпретируемые как двоичные данные; каждый бит соответствует файловому манипулятору.
Типичный вызов select выглядит так:
$rin = ''; # Инициализировать маску
vec($rin, fileno(SOCKET), 1) = 1; # Пометить SOCKET в $rin
# Повторить вызовы vec() для каждого проверяемого сокета
timeout = 10; # Подождать 10 секунд
$nfound = select($rout = $rin, undef, undef, $timeout);
if (vec($rout, fileno(socket),1)) {
# В SOCKET имеются данные для чтения
}
Функция select вызывается с четырьмя аргументами. Три из них представляют собой битовые маски:
первая проверяет в манипуляторах наличие непрочитанных данных в манипуляторах; вторая — возможность
безопасной записи без блокировки; третья — наличие в них исключительных состояний. Четвертый аргумент
определяет максимальную длительность ожидания в секундах (может быть вещественным числом).
Функция модифицирует передаваемые ей маски, поэтому при выходе из нее биты будут установлены
лишь для манипуляторов, готовых к вводу/выводу. Отсюда один стандартный прием — входная маска
($rin в предыдущем примере) присваивается выходной ($rout), чтобы вызов select изменил только
$rout и оставил $rin в прежнем состоянии.
Нулевой тайм-аут определяет режим опроса (проверка без блокировки). Некоторые начинающие
программисты не любят блокировки, и в их программах выполняется «занятое ожидание» (busy-wait) —
программа в цикле выполняет опрос, снова и снова. Когда программа блокируется, операционная
система понимает, что процесс ждет ввода, и передает процессорное время другим программам до
появления входных данных. Когда программа находится в «занятом ожидании», система не оставляет
ее в покое, поскольку программа всегда что-то делает — проверяет ввод! Иногда опрос действительно
является правильным решением, но гораздо чаще это не так. Тайм-аут, равный
, означает
отсутствие тайм-аута, поэтому ваша программа терпеливо блокируется до появления ввода.
Поскольку select использует битовые маски, которые утомительно создавать и трудно интерпретировать,
в решении используется стандартный модуль IO::Select. Он обходит работу с битовыми масками и,
как правило, более удобен.
См. такжеОписание функций
send,
recv и
vec
Proverte kod v komentariyah gde pro list tam oshibki detskie
Оставить комментарий:
|
|