我用Perl实现了一个小型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不是线程安全,但我不确定情况是否仍然如此。
有没有人知道这样做的可能性呢?或许这是可能的,但我是用错误的方式处理的.
谢谢,克里斯
编辑:Debian8.3与Perl5.20.2一起使用。Net::SSLeay为1.72,IO::Socket::SSL为2.024。OpenSSL 1.0.1k
编辑:将代码示例更改为功能齐全的小示例程序。
发布于 2016-02-25 18:42:14
TL;TR:
不要将已建立的SSL套接字复制到另一个线程中。
详细信息:
您可以将主线程中的SSL套接字接受到$cl中,然后创建一个在新套接字上工作的新线程。实际上,这意味着您有相同的文件描述符(内核)、相同的OpenSSL数据结构(用户空间),但是使用这个单一数据结构的两个Perl变量(perl线程不共享-因此Perl部分被复制)。
这只会带来麻烦,因为您随后隐式地关闭了主程序中的套接字($cl超出了作用域),但是继续在客户端线程中使用它。主线程中的关闭会导致SSL关闭,然后释放底层OpenSSL结构。因此,客户端线程中的$cl指向一些释放的内存,从而导致崩溃。实际上,如果使用分叉而不是线程,也会得到类似的结果(没有崩溃),因为主进程中仍然存在SSL关机,因此对等方认为套接字已关闭,子节点将无法进一步使用该套接字。
与其在主线程中执行SSL接受,不如将每个SSL活动移动到客户端线程中。这将通过对普通套接字对象执行accept,然后在客户端线程中将其升级到SSL来完成。无论如何,这是首选的方法,有关详细信息,请参阅IO::Socket::SSL文档中的基本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 $@;https://stackoverflow.com/questions/35632515
复制相似问题