首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Perl,IO::Socket::SSL,多线程

Perl,IO::Socket::SSL,多线程
EN

Stack Overflow用户
提问于 2016-02-25 16:10:03
回答 1查看 1.2K关注 0票数 1

我用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一样。但是当我第一次读的时候,我得到了一个分割错误。

代码语言:javascript
复制
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

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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服务器

最后,您的代码将被更改如下:

代码语言:javascript
复制
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 $@;
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35632515

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档