Perl:实现套接字编程( system() 从不返回)

han*_*ish 1 sockets perl fork openssh system

我的目标:实现套接字编程,如果服务器未安装在远程机器上,客户端会尝试连接到服务器,客户端(主机)将 tar 文件传输到服务器(目标)机器和 perl 脚本。这个 perl 脚本解压文件夹并运行一个脚本(服务器 perl 脚本),现在的问题是:这个服务器脚本必须永远运行(多个客户端),直到机器重新启动或发生一些不愉快的事情。所以脚本运行正常:但是由于它一直在运行,因此控件不会返回到客户端,客户端将再次尝试连接到服务器(在某个预定义的套接字上),所以基本上我希望以某种方式运行服务器但带回控制到我的主机,在这种情况下是客户端。

这是代码:

my $sourcedir = "$homedir/host_client/test.tar";
my $sourcedir2 = "$homedir/host_client/sabkuch.pl";
my $remote_path = "/local/home/hanmaghu";

# Main subroutines
my $ssh = Net::OpenSSH->new ( $hostmachine, user =>$username, password => $password);
$ssh->scp_put($sourcedir,$sourcedir2,$remote_path)
     or die "scp failed \n" . $ssh->error;
# test() is similar to system() in perl openssh package
my $rc = $ssh->test('perl sabkuch.pl'); 
# check if test function returned or not -> this is never executed
if ($rc == 1) {
    print "test was ok , server established \n";
}
else {
    print "return from test = $rc \n";
}
exit;
Run Code Online (Sandbox Code Playgroud)

调用我们的服务器脚本的另一个脚本是:

#!/usr/bin/perl
use strict;
use warnings;              
system('tar -xvf test.tar');
exec('cd utpsm_run_automation && perl utpsm_lts_server.pl');
#system('perl utpsm_lts_server.pl'); 
# Tried with system but in both cases it doesn't return, 
# this xxx_server.pl is my server script
exit;
Run Code Online (Sandbox Code Playgroud)

服务器脚本是:

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::INET;

#flush after every write
$| =1;

my $socket = new IO::Socket::INET (
    LocalHost => '0.0.0.0',
    LocalPort => '7783', 
    Proto => 'tcp',
    Listen => 5,
    Reuse => 1
);
die "cannot create socket $! \n" unless $socket;
print "server waiting for client on port $socket->LocalPort \n";

while (1)
{
    # waiting for new client connection
    my $client_socket = $socket->accept();

    # get info about new connected client
    my $client_address = $client_socket->peerhost();
    my $client_port = $client_socket->peerport();
    print "connection from $client_address:$client_port \n";

    # read upto 1024 characters from connected client
    my $data = "";
    $client_socket->recv($data,1024);
    print "rceeived data = $data";

    # write response data to the connected client
    $data = "ok";
    $client_socket->send($data);

    # notify client response is sent
    shutdown($client_socket,1);
}
$socket->close();
Run Code Online (Sandbox Code Playgroud)

请帮助如何执行此操作:就设计而言,这是我想要的,但在实施时遇到此问题,我可以做一些其他的解决方法吗?

zdi*_*dim 5

根据要求更新   添加fork+exec示例。这里我推荐system('cmd &')


简而言之,您的“驱动程序”sabkuch.pl使用exec-- 永远不会返回来启动服务器。从exec

“exec”函数执行系统命令并且永不返回;...

(重点来自引用的文档。)一旦使用 an exec,进程中运行的程序就会另一个程序替换,请参见exec wiki。如果该服务器继续运行,而exit您从未访问过,除非出现错误。请参阅exec上面的Perl链接。

所以你$ssh->test()会永远阻塞(好吧,直到服务器以某种方式退出)。您需要一种非阻塞方式来启动服务器。这里有一些选项

  • 在后台运行驱动程序

    my $rc = $ssh->test('perl sabkuch.pl &');
    
    Run Code Online (Sandbox Code Playgroud)

    这会启动一个单独的子外壳并sabkuch.pl在其中生成,然后返回控制并test可以完成。的sabkuch.pl运行exec,从而转化成了其它程序(服务器),无限期地运行。见Background processes in perlipc。另请参阅it in perlfaq8,以及那里的许多好的链接。请注意,不需要perl ...ifsabkuch.pl可以执行。

  • 查看是否Net::OpenSSH有执行命令的方法,使其不会阻塞。

  • “即发即忘”的一种方法是在子进程中fork然后exec在父进程中退出(在这种情况下)。然后还有更多需要考虑。在 中可以找到大量(强制性)信息perlipc。其他地方的例子比比皆是,搜索fork 和 exec。这不应该掉以轻心,因为错误会导致奇怪的行为和无法解释的后果。这是一个例子。

    #!/usr/bin/perl
    use strict;
    use warnings;               
    system('tar -xvf test.tar') == 0  or die "Error with system(...): $!";
    
    my $pid = fork;
    die "Can't fork: $!" if not defined $pid;
    # Two processes running now. For child $pid is 0, for parent large integer
    
    if ($pid == 0) {  # child, parent won't get into this block
        exec('cd utpsm_run_automation && perl utpsm_lts_server.pl');
        die "exec should've not returned: $!";
    }   
    # Can only be parent here since child exec-ed and can't get here. Otherwise,
    # put parent-only code in else { } and wait for child or handle $SIG{CHLD}
    exit;
    
    Run Code Online (Sandbox Code Playgroud)

    分叉时通常需要关注的是wait儿童。如果我们不愿意,这可以通过双分叉或处理 SIGCHLD(参见waitpid)来解决,例如。请研究perlfaq8上面的链接,Signals in perlipc所有使用的调用的文档,以及您可以使用的所有其他内容。在这种情况下,父进程应该首先退出,然后子进程被重新作为父进程init,一切都很好。该exec-ed过程得到相同的$pid,但由于cd将触发壳(sh -c cd)服务器将最终与一个不同的PID运行。

    有了system('command &')我们就不用担心等孩子了。


这仅与您的直接问题有关,与显示的代码的其余部分无关。