Perl,IO::Socket::SSL,多线程
我已经用Perl实现了一个小型Web服务器。它正在使用IO::Socket::INET和IO::Socket::SSL并行进行侦听 如果HTTP端口出现连接,我将启动一个线程并处理IO::Socket::INET引用 由于Net::SSLeay中的线程限制(文档中的IO::Socket::SSL表示,1.43以下的线程不安全),我没有并行化SSL。我只是在相同的上下文中调用处理函数。 在HTTP的并行化情况下,处理程序函数是threads函数 所有这些在更长的时间内都像预期的那样起作用 我现在已经更新了我的系统。现在,我的Net::SSLeay是1.72,我也尝试并行化SSL,这与我处理HTTP的方式相同。但是我在第一次读的时候就遇到了一个分段错误Perl,IO::Socket::SSL,多线程,perl,sockets,ssl,Perl,Sockets,Ssl,我已经用Perl实现了一个小型Web服务器。它正在使用IO::Socket::INET和IO::Socket::SSL并行进行侦听 如果HTTP端口出现连接,我将启动一个线程并处理IO::Socket::INET引用 由于Net::SSLeay中的线程限制(文档中的IO::Socket::SSL表示,1.43以下的线程不安全),我没有并行化SSL。我只是在相同的上下文中调用处理函数。 在HTTP的并行化情况下,处理程序函数是threads函数 所有这些在更长的时间内都像预期的那样起作用 我现在已
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::Handle;
使用Fcntl(“F_GETFL”、“F_SETFL”、“O_NONBLOCK”);
使用时间:雇佣(“usleep”);
使用插座;
使用IO::Socket::SSL;
使用线程;
标准输出->自动刷新();
my$port=“4433”;
我的$cer=“cer.cer”;
my$key=“key.key”;
我的$sock=IO::Socket::SSL->new(Listen=>SOMAXCONN,LocalPort=>$port,
阻塞=>0,超时=>0,ReuseAddr=>1,SSL\U服务器=>1,
SSL\U证书文件=>$cer,SSL\U密钥文件=>$key)或die$@;
我的$WITH_THREADS=0;#开关!!
对于(;;)
{
评估
{
my$cl=$sock->accept();
如果($cl)
{
打印(“\nssl connect”);
如果($WITH_THREADS==0)
{
#这不是多线程
客户(cl);
}
否则{
#使用多线程
my$th=threads->create(\&client,$cl);
$th->detach();
}
}
}##评估
如果($@)
{
打印“ex:$@”;
出口(1);
}
美国LEEP(100000);
}永远
次级客户#工人
{
我的$cl=班次;
#解除封锁
我的$flags=fcntl($cl,F_GETFL,0)或die$!;
fcntl($cl,F|U SETFL,$flags | O|U NONBLOCK)或die$!;
打印“\n”。$cl->peerhost.”/“$cl->peerport;
我的$ret=“”;
对于(我的$i=0;$i<100;$i++)
{
$ret=$cl->read(my$recv,5000);
#如果使用线程,则此处出现错误!
if(定义($ret)和长度($recv)>0)
{
打印“\n收到的$ret字节”;
}
其他的
{
打印“\n无数据”;
}
usleep(200000);
}
打印“\nend client”;
$cl->close();
}
我也读过一些帖子,他们说IO::Socket::SSL不是线程安全的,但我不确定情况是否仍然如此
有人知道这样做是否可行吗?也许这是可能的,但我处理它的方式是错误的
谢谢,
克里斯
编辑:我在Perl 5.20.2中使用Debian 8.3。
Net::SSLeay是1.72,IO::Socket::SSL是2.024。
OpenSSL 1.0.1k
编辑:将代码示例更改为功能齐全的小示例程序。TL;TR:不要将已建立的SSL套接字复制到另一个线程中 详细信息:
您将主线程中的SSL套接字接受为
$cl
,然后创建一个在新套接字上工作的新线程。实际上,这意味着您拥有相同的文件描述符(内核)、相同的OpenSSL数据结构(用户空间),但有两个使用此单一数据结构的Perl变量(Perl线程不共享任何内容,因此Perl部分是重复的)
这只会导致问题,因为您随后会隐式关闭主线程中的套接字($cl
超出范围),但会继续在客户端线程中使用它。主线程中的关闭会导致SSL关闭,然后释放底层OpenSSL结构。因此,客户机线程中的$cl
指向导致崩溃的一些已释放内存。如果使用forking而不是threading,实际上也会得到类似的结果(没有崩溃),因为在主进程中仍然存在SSL关闭,因此对等方认为套接字已关闭,子进程将无法进一步使用套接字
您不应该在主线程中执行SSL accept,而应该将每个SSL活动都移动到客户端线程中。这将通过在普通套接字对象上执行accept,然后在客户端线程中将其升级到SSL来完成。无论如何,这是首选方法,有关详细信息,请参阅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 $@;
我对工作前的想法不熟悉。你能简单地解释一下你是如何处理这个问题的吗?你必须在一个地方(套接字侦听器)进行监听,然后把它转发给一个工作人员。我可以想象在接受之后(接受之后)会有一个fork,但是在接受之前我该怎么做呢?对不起,请忽略我的评论。我刚刚看过httpd,它的前期工作背后的想法是什么。如果它不能与perl线程一起工作,也许我会考虑它。但我更喜欢当前的想法。@chris:我是IO::Socket::SSL的维护者。我不知道线程支持有任何问题。您可以添加OS、Perl版本和IO::Socket::SSL版本吗?如果你能提供一个最小但完整的例子来重现这个问题,我可以仔细看看。谢谢你,太好了。我可以把详细信息发送到你的数据包维护者地址吗。。。我懂了。太完美了!非常感谢你!!