Perl:分叉WSS连接会导致子级退出时套接字关闭(WS不会发生)

时间:2018-10-05 13:08:13

标签: perl websocket

(根据下面的评论进行编辑以提供减少的测试用例)

我正面临一个奇怪的情况,如果我派生一个“ WSS”连接来发送消息,则子级退出时套接字将关闭。但是,当我分叉处理“ WS”连接时,子项退出时该连接仍保持打开状态。

  • 服务器详细信息:Perl 5.26,Ubuntu 16
  • 客户端详细信息:Perl 5.18,OSX

服务器代码:

use Net::WebSocket::Server;
use IO::Socket::SSL;

$SIG{CHLD}='IGNORE';

my $enable_ssl = 1; # If you make this one the problem reveals itself

# you need to point this to your own certs
my $ssl_cert_file =  "/etc/letsencrypt/live/mydomain/fullchain.pem";
my $ssl_key_file =  "/etc/letsencrypt/live/mydomain/privkey.pem";

# To show the problem, all I'm doing is I'm forking and sending current time
sub process {
    my $serv = shift;
    my $pid = fork();
    if ($pid == 0 ) {
        print ("fork start\n");
        $_->send_utf8(time) for $serv->connections;
        print ("fork end\n");
        exit 0;
    }
}

my $ssl_server;

if ($ssl_enable) {
$ssl_server = IO::Socket::SSL->new(
              Listen        => 10,
              LocalPort     => 9000,
              Proto         => 'tcp',
              Reuse     => 1,
              ReuseAddr     => 1,
              SSL_cert_file => $ssl_cert_file,
              SSL_key_file  => $ssl_key_file
            );
 }

Net::WebSocket::Server->new(
    listen => $enable_ssl? $ssl_server: 9000,
    tick_period=>5,
    on_tick=> sub {
        my ($serv) = @_;
        process($serv);
        #$_->send_utf8(time) for $serv->connections;
    },
 )->start;

这是客户端代码:

my $client = AnyEvent::WebSocket::Client->new;

# replace with your server
$client->connect("wss://myserver:9000")->cb(sub {
  our $connection = eval { shift->recv };
  if($@) {
    print ("connection error");
    warn $@;
    return;
  }

  # recieve message from the websocket...
  $connection->on(each_message => sub {
    my($connection, $message) = @_;
    my $msg = $message->body;
    print ("GOT $msg\n");
  });


});

AnyEvent->condvar->recv;

预期的行为

客户端将继续显示时间戳

观察到的行为

客户端获得第一条消息并打印。 当服务器退出其分支时,客户端停止获取更多消息,并且连接终止

如何使其工作

我们有两个选择:

  1. 不要在服务器中派生。直接在处理子中发送消息
  2. 不使用SSL

因此,我的结论是SSL + fork ==问题。

有想法吗?

1 个答案:

答案 0 :(得分:3)

  

因此,我的结论是SSL + fork ==问题。

是的,问题首先是进行SSL握手,然后进行分叉。这样,将在父级中创建用户空间SSL状态,并在子级中创建分叉,并且这两个SSL状态在发送或接收的第一个SSL数据上不同步。这意味着不可能从两个进程中处理相同的SSL套接字。

如果确实有必要父级和子级进程都使用与同级的相同SSL连接,而不是子级必须将父级用作“代理”,即子级不直接与SSL对等方通信,但是子级需要与父级进行简单通信(例如,使用套接字对),然后父级可以将通信转发给SSL对等方。这样,SSL状态仅在父进程中维护。

但是,考虑到单个连接一次只能处理一条消息,则可以不对单个连接进行分叉,而是为每个连接分派一个子代,然后处理该连接中的所有消息。在这种情况下,可以通过在父级中侦听TCP而不是SSL套接字,派生on_connect,然后使用IO::Socket::start_SSL在客户端中将连接升级到SSL,从而在子级中完全完成SSL握手。这还将具有以下优点:阻塞的SSL握手(涉及多次往返,因此需要一些时间)将在派生的子代中完成,而不会成为父代代币。