Добрый день! Сегодня я бы хотел описать принцип работы с сокетами в Perl. Вообще говоря, поддержка сокетов существует лишь в современных языках, ориентированных на протоколы Internet. Perl оказался в их числе (еще бы, Perl и Internet, имхо, просто созданы друг для друга). По определению, сокеты - конечные пункты в процессе обмена данными. Для простоты понимания, возьмем систему - "розетка-штепсель". Пока штепсель не воткнется в розетку, ток не пойдет по проводам и не замкнет цепь. Также устроены и сокеты. Одна сторона ("розетка") обеспечивает подключение методом слежения порта, а другая ("вилка") способна подключится к первой стороне, и принимать/отправлять данные.
Как известно сокеты делятся на два типа: потоковые (TCP, SOCK_STREAM) и датаграммные (UDP, SOCK_DGRAM). Первый тип обеспечивает надежную коммуникацию без каких-либо потерь данных. Второй тип не гарантирует полную передачу данных, но зато наиболее удобен в плане скорости (нет проверки на точную доставку пакета).
По областям сокеты делят на сокеты Интернета (параметры: IP и порт, PF_INET) и сокеты UNIX (параметры: файл-сокет в системе, PF_UNIX).
Perl, как любой уважающий себя язык, содержит встроенные функции для создания сокетов, но я думаю, что внедрение модулей и объектно-ориентированного программирования в Perl никому не повредило, поэтому буду использовать в описании мой любимый модуль IO::Socket. В данной статье я буду рассматривать потоковые сокеты Интернет (IO::Socket::INET).
Создание сокета
Для создания сокета, в первую очередь нужно подключить в скрипт модуль IO::Socket (фактическое местоположение: IO/Socket.pm относительно директории с библиотеками и модулями Perl). Затем нужно заполнить важные поля объекта, с помощью вызова конструктора new(). Обязательным полем к заполнению является параметр сокета, как написано выше - это IP адрес (который может фигурировать, как hostname и port). Например такая конструкция будет вполне верна (для создания клиента TCP):
$socket = IO::Socket::INET->new("www.ru:80")
Хотя, иногда мало одного поля, бывает нужно описать некоторые другие свойства сокета (таймаут на подключение, протокол). Вот наиболее полное заполнение параметров конструктора для TCP-клиента:
$socket = IO::Socket::INET->new(PeerAddr=> "www.ru",
PeerPort => 80,
Proto => 'tcp',
Timeout => 50,
Type => SOCK_STREAM) || die "error $!\n";
Где переменная $! сообщит ошибку в том случае, если подключение не состоялось.
Иными будут выглядеть параметры конструктора при написании TCP-сервера (который открывает порт для подключения). Его структура будет примерно следующей:
$socket = IO::Socket::INET->new(LocalPort => 31337,
Type => 'tcp',
Reuse => 1,
Listen => 10) || die "error $!\n";
Где поля имеют следующее значение:
LocalPort - непосредственно порт, который открывается для слежения.
Reuse - способность сокета перезапускаться при каждом подключении.
Listen - максимальное количество открытых потоков сервера.
Подключение в режиме клиента происходит сразу же после создания сокета. В режиме сервера
клиент считается подключенным после достижения метода $socket->accept. Это выглядит примерно следующим образом:
while($client = $socket->accept()) {
print $client "Welcome to my server\n";
}
Как видно из примера, метод сокета возвращает закрепленный дескриптор клиента, с помощью которого можно осуществлять прием и передачу данных непосредственно между ним и сервером.
Получить ip-адрес клиента можно следующим алгоритмом: идентифицировать клиент, и получить из этих данных его ip-адрес. Это будет выглядеть примерно следующим образом:
$my($prip)=getpeername($client);
($port,$ipaddr)=unpack_sockaddr_in($prip);
$ipaddr= inet_ntoa($ipaddr);
Прием/передача данных
Самый простой способ приема и передачи данных через сокет, являются стандартные операторы print и получение данных из STDIN - <>.
Рассмотрим небольшой пример, а именно передачу в сокет hello world и прием оттуда ответа.
$socket = IO::Socket::INET->new("www.ru:80");
print $socket "Hello world\n";
chomp($answer=<$socket>);
Символ сброса строки "\n" обязателен, так как без него данные не смогут попасть на сервер.
Добиться передачи данных без их буферизации можно отключив ее методом autoflush: $socket->autoflush(1). После этого данные будут автоматически определены без предварительной буферизации.
Закрытие сокета происходит файловой процедурой close(), параметром которой является идентификатор сокета.
Вот самые простые сведения о работе с сокетами в Perl. Далее, я постараюсь описать теорию о разветвляющихся многопоточных серверах и демонах, а также о межпроцессорном взаимодействии и обработке сигналов при создании сокетов.