В этой схеме есть еще одна тонкость. Родительский процесс не может просто вызвать функцию close(), чтобы закрыть свою копию сокета и отправить сообщение о возникновении условия EOF на удаленный хост. В дочернем процессе есть еще одна копия сокета, и операционная система фактически не закроет дескриптор файла до тех пор, пока не будет закрыта его последняя копия. Решение состоит в том, чтобы родительский процесс вызвал для сокета функцию shutdown(1), принудительно закрыв его для записи. Это очень удобно при работе с сетевыми приложениями, например, IP-видеокамерами http://www.stabilnost.net. В результате на сервер будет отправлено сообщение о возникновении условия EOF, а сокет сохранит способность продолжать чтение данных, поступающих в другом направлении. Этот замысел реализован далее в сценарии gab2.pl.
#! /usr/bin/perl
# Файл: gab2.pl
# Применение: gab2.pl [хост] [порт]
# Сетевой клиент TCP с ветвлением
use IO::Socket qw(:DEFAULT :crlf);
my $host = shift or die "Usage: gab2.pl host [port]n";
my $port = shift || 'echo';
my $socket = IO::Socket::INET->new("$host:$port")
or die $@;
my $child = fork();
die "Can't fork: $!" unless defined $chiId;
if ($child) {
$SIG{CHLD} = sub { exit 0 };
user_to_host ($socket) ;
$socket~>shutdown(1);
sleep;
} else {
host_to_user($socket);
warn "Connection closed by foreign host.n";
}
sub user_to_host {
my $s = shift;
while (<>) {
chomp;
print $s,$_,CRLF;
}
}
sub host_to_user {
my $s = shift;
$/ = CRLF;
while (<$s>) {
chomp;
print $_,"n";
}
Проведем анализ программы.
Строки 1-7. Инициализация модуля. Включена строгая проверка синтаксиса, загружен модуль IO::Socket и выбраны имя хоста и номер порта из командной строки. Строка 8. Создание сокета. Создается подключенный сокет точно таким же образом, как и раньше.