gpt4 book ai didi

Perl、IO::Socket::SSL、多线程

转载 作者:太空宇宙 更新时间:2023-11-03 13:16:17 30 4
gpt4 key购买 nike

我用 Perl 实现了一个小型网络服务器。它通过 IO::Socket::INET 和 IO::Socket::SSL 并行监听。

如果连接出现在 HTTP 端口,我将启动一个线程并处理 IO::Socket::INET 引用。

由于 Net::SSLeay 中的线程限制(IO::Socket::SSL 在文档中说在 1.43 以下不是线程安全的)我没有并行化 SSL。我只是在同一上下文中调用处理函数。在 HTTP 的并行化情况下,处理函数是线程函数。

所有这一切都按预期工作了更长的时间。

我现在已经更新了我的系统。现在我的 Net::SSLeay 是 1.72,我也尝试并行化 SSL - 就像我对 HTTP 所做的一样。但是我在第一次读取时遇到了段错误。

use strict;
use warnings;
use IO::Handle;
use Fcntl ("F_GETFL", "F_SETFL", "O_NONBLOCK");
use Time::HiRes ("usleep");
use Socket;
use IO::Socket::SSL;
use threads;
STDOUT->autoflush ();

my $port = "4433";
my $cer = "cer.cer";
my $key = "key.key";

my $sock = IO::Socket::SSL->new (Listen => SOMAXCONN, LocalPort => $port,
Blocking => 0, Timeout => 0, ReuseAddr => 1, SSL_server => 1,
SSL_cert_file => $cer, SSL_key_file => $key) or die $@;

my $WITH_THREADS = 0; # the switch!!

for (;;)
{
eval
{
my $cl = $sock->accept ();
if ($cl)
{
print ("\nssl connect");

if ($WITH_THREADS == 0)
{
# this is no multi-threading
client ($cl);
}
else {
# with multithreading
my $th = threads->create (\&client, $cl);
$th->detach ();
}
}
}; # eval
if ($@)
{
print "ex: $@";
exit (1);
}
usleep (100000);
} # forever


sub client # worker
{
my $cl = shift;

# unblock
my $flags = fcntl ($cl, F_GETFL, 0) or die $!;
fcntl ($cl, F_SETFL, $flags | O_NONBLOCK) or die $!;

print "\n" . $cl->peerhost . "/" . $cl->peerport;

my $ret = "";

for (my $i = 0; $i < 100; $i ++)
{
$ret = $cl->read (my $recv, 5000);
# faults here if with threads!

if (defined ($ret) && length ($recv) > 0)
{
print "\nreceived $ret bytes";
}
else
{
print "\nno data";

}
usleep (200000);
}
print "\nend client";
$cl->close ();
}

我也读过一些帖子,他们说 IO::Socket::SSL 不是线程安全的,但我不确定情况是否仍然如此。

有谁知道这样可行吗?或者也许这是可能的,但我处理它的方式不对......

谢谢,克里斯

编辑: 我将 Debian 8.3 与 Perl 5.20.2 结合使用。Net::SSLeay 为 1.72,IO::Socket::SSL 为 2.024。OpenSSL 1.0.1k

编辑:将代码示例更改为功能齐全的小示例程序。

最佳答案

长话短说:
不要将已建立的 SSL 套接字复制到另一个线程中。

详情:
您将主线程中的 SSL 套接字接受到 $cl 中,然后创建一个在新套接字上工作的新线程。实际上,这意味着您拥有相同的文件描述符(内核)、相同的 OpenSSL 数据结构(用户空间),但使用此单一数据结构的两个 Perl 变量(perl 线程不共享任何内容 - 因此 Perl 部分是重复的)。

这只会导致麻烦,因为您随后在主线程中隐式关闭套接字($cl 超出范围)但继续在客户端线程中使用它。主线程中的关闭导致 SSL 关闭,然后释放底层 OpenSSL 结构。因此,客户端线程中的 $cl 指向一些导致崩溃的释放内存。如果你使用 fork 而不是线程,你实际上也会得到这样的东西(没有崩溃),因为在主进程中仍然完成了 SSL 关闭,所以对等方认为套接字已关闭并且 child 将无法进一步使用 socket 。

与其在主线程中执行 SSL 接受,不如将每个 SSL 事件移动到客户端线程中。这将通过对普通套接字对象执行接受,然后在客户端线程中将其升级为 SSL 来完成。无论如何,这是首选方式,请参阅 Basic SSL server在 IO::Socket::SSL 文档中了解详细信息。

最后你的代码会变成这样:

my $port = "4433";
my $cer = "cer.cer";
my $key = "key.key";

# don't create a SSL socket but an INET socket
my $sock = IO::Socket::IP->new (
Listen => SOMAXCONN, LocalPort => $port, Blocking => 0, ReuseAddr => 1
) or die $!;

my $WITH_THREADS = 1; # the switch!!
....
sub client # worker
{
my $cl = shift;
# upgrade INET socket to SSL
$cl = IO::Socket::SSL->start_SSL($cl,
SSL_server => 1,
SSL_cert_file => $cer,
SSL_key_file => $key
) or die $@;

关于Perl、IO::Socket::SSL、多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35632515/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com