У меня есть приложение, которое устанавливает соединения с различным оборудованием в нашей сети по требованию, выполняет несколько команд, анализирует выходные данные и отправляет отчеты пользователю через AJAX / JSON / Perl. В Perl я использую NET :: Telnet / :: Cisco , а также время от времени SSH-соединение через порождение дочернего процесса и передачу его в NET :: Telnet .
Я хочу улучшить приложение, создав своего рода держатель сеанса telnet, который будет поддерживать соединения после того, как они открыты в течение заданного периода времени или до истечения времени ожидания. Идея состоит в том, чтобы сократить повторное подключение к этим устройствам и разрешить другим запросам использовать сеанс telnet, который был создан ранее (при условии, что он не используется и все еще активен).
Я начал писать файл perl, используя IO :: Socket :: UNIX , и могу хранить соединения без проблемы, и в основном другой файл будет использовать сокет, созданный для доступа или создания нового соединения. Проблема, с которой я сталкиваюсь, заключается в следующем: если два запроса попадают в один и тот же сокет одновременно, то какой бы один из них ни пришел первым, второй будет вынужден ждать обработки первого.
Я начал экспериментировать с использованием потоков , но если я это сделаю, я не смогу передать объект NET :: Telnet обратно в исходный / родительский поток.
У кого-нибудь есть идеи, как этого добиться? Может быть, есть приложение, которое будет действовать как держатель сеанса, с которым я могу взаимодействовать?
UPDATE
Я использовал POE в соответствии с предложением одного комментатора, и хотя это частично выполняет то, что я ищу, оно не позволяет «серверу» одновременно обслуживать несколько соединений.
Сценарий:
Два пользователя нажимают «отправить» на переднем конце довольно близко друг к другу. Сначала запрос пользователя A достигает сервера, но он пытается подключиться к устройству, на ответ которого уходит много времени. Таким образом, пользователю B придется подождать, пока соединение пользователя A не будет установлено, прежде чем запрос пользователя B будет даже запущен. По существу
, мне нужно иметь возможность обслуживать одновременные запросы, используя пул соединений, не создавая задержки для кого-либо еще только потому, что парень, который первым пришел туда, пытается подключиться к этому устройству slooooow ..
Ниже приведен мой код, который, если я так сделаю, будет выполняться в фоновом режиме. Отпечатки для моей отладки.
#!/usr/bin/perl -w
use strict;
use JSON;
use Net::Telnet;
use IO::Socket::UNIX qw( SOCK_STREAM SOMAXCONN );
my $socketPath = '/tmp/telnetproxy';
unlink($socketPath);
my $listener = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Local => $socketPath,
Listen => SOMAXCONN)
or die ("Cannot create server socket: $!\n");
my $clientNum = 0;
our $json = JSON->new->allow_nonref;
our $conns = {};
print "Server Initiated...\n";
while (1) {
print " - Inside while loop...\n";
my $socket = $listener->accept();
connectToDevice(++ $clientNum, $socket);
}
sub connectToDevice {
my $connectionNum = shift;
my $socket = shift;
print " - Inside 'connectToDevice'\n";
print " - Connection #$connectionNum started...\n";
my $input;
my $connId = 0;
my (@argsRaw, $args, @argHold);
my $numOfConnections = keys %$conns;
my $deviceProperties = {
ipAddress => undef,
username => undef,
password => undef,
method => 'telnet'
};
print " - waiting for input...\n";
# Receive input for arguments.
chomp( $input = <$socket> );
print " - input received...\n";
## Turn string into a HASHREF
$args = from_json($input);
foreach (keys %$args) {
print "\t$connectionNum: $_ => $args->{$_}\n";
if (/^host$/i) { #---- Host IP given ($self->{_hostIp{
if (verifyIp($args->{$_})) {
$deviceProperties->{ipAddress} = $args->{$_};
} else {
}
}
elsif (/^method$/i) { # Ckt type... very important for how we ts
$deviceProperties->{method} = $args->{$_};
}
elsif (/^(username|user|u)$/i) { # username to log in with
$deviceProperties->{username} = $args->{$_};
}
elsif (/^(password|pass|p)$/i) { # password
$deviceProperties->{password} = $args->{$_};
}
}
print " - Num of connections: $numOfConnections\n";
if ($numOfConnections > 0) {
## Look through existing connections
## 1) If we have an available connection, use it
## 2) If not, create a new connection.
print " - Checking existing connections...\n";
foreach my $connKey ( keys %$conns ) {
if ($conns->{$connKey}->{host} eq $deviceProperties->{ipAddress} && $conns->{$connKey}->{locked} == 0 && testConnection($connKey)) {
$connId = $connKey;
print "\tconnection #$connKey... VALID, using it\n";
last;
} else {
print "\tconnection #$connKey... not valid\n";
}
}
} else {
print " - No existing connections, creating a new one ...\n";
}
if ($connId == 0) {
$connId = $connectionNum;
$conns->{$connectionNum} = {
host => $deviceProperties->{ipAddress},
locked => 1
};
$conns->{$connectionNum}->{conn} = connectToHost($deviceProperties, "blab_$connectionNum");
print " - Created a new connection, a suitable existing connection was not found.\n";
}
print " - Waiting for command.. ";
chomp( my $line = <$socket> );
print "DONE\n";
my @out = $conns->{$connId}->{conn}->cmd($line);
print " - Sent '$line' to device\n";
my $numOfLines = @out;
print " - $numOfLines lines retrieved\n";
$conns->{$connId}->{locked} = 0;
print " - This run done....\n\n";
return;
}
sub testConnection {
my $connectionNum = shift;
print " -- Testing connection $connectionNum: ";
my @out = $conns->{$connectionNum}->{conn}->cmd(String => '!', Timeout => 2);
print "[";
print @out;
print "]";
if (@out > 0) {
print " ---- Good\n";
return 1;
} else {
delete $conns->{$connectionNum};
print " ---- No good\n";
return 0;
}
}