(根据下面的评论进行编辑以提供减少的测试用例)
我正面临一个奇怪的情况,如果我派生一个“ WSS”连接来发送消息,则子级退出时套接字将关闭。但是,当我分叉处理“ WS”连接时,子项退出时该连接仍保持打开状态。
服务器代码:
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;
预期的行为
客户端将继续显示时间戳
观察到的行为
客户端获得第一条消息并打印。 当服务器退出其分支时,客户端停止获取更多消息,并且连接终止
如何使其工作
我们有两个选择:
因此,我的结论是SSL + fork ==问题。
有想法吗?
答案 0 :(得分:3)
因此,我的结论是SSL + fork ==问题。
是的,问题首先是进行SSL握手,然后进行分叉。这样,将在父级中创建用户空间SSL状态,并在子级中创建分叉,并且这两个SSL状态在发送或接收的第一个SSL数据上不同步。这意味着不可能从两个进程中处理相同的SSL套接字。
如果确实有必要父级和子级进程都使用与同级的相同SSL连接,而不是子级必须将父级用作“代理”,即子级不直接与SSL对等方通信,但是子级需要与父级进行简单通信(例如,使用套接字对),然后父级可以将通信转发给SSL对等方。这样,SSL状态仅在父进程中维护。
但是,考虑到单个连接一次只能处理一条消息,则可以不对单个连接进行分叉,而是为每个连接分派一个子代,然后处理该连接中的所有消息。在这种情况下,可以通过在父级中侦听TCP而不是SSL套接字,派生on_connect
,然后使用IO::Socket::start_SSL
在客户端中将连接升级到SSL,从而在子级中完全完成SSL握手。这还将具有以下优点:阻塞的SSL握手(涉及多次往返,因此需要一些时间)将在派生的子代中完成,而不会成为父代代币。