如何在Ruby中创建双向SSL套接字 [英] How can I create a two-way SSL socket in Ruby

查看:204
本文介绍了如何在Ruby中创建双向SSL套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个客户端Ruby库,该库连接到服务器并等待数据,但是还允许用户通过调用方法来发送数据.

I am building a client Ruby library that connects to a server and waits for data, but also allows users to send data by calling a method.

我使用的机制是拥有一个初始化套接字对的类,如下所示:

The mechanism I use is to have a class that initializes a socket pair, like so:

def initialize
  @pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0)
end

我允许开发人员调用以将数据发送到服务器的方法如下:

The method that I allow developers to call to send data to the server looks like this:

def send(data)
  @pipe_w.write(data)
  @pipe_w.flush
end

然后我在一个单独的线程中循环,在该线程中,我从连接到服务器的socket@pipe_r中进行选择:

Then I have a loop in a separate thread, where I select from a socket connected to the server and from the @pipe_r:

def socket_loop
  Thread.new do
    socket = TCPSocket.new(host, port)

    loop do
      ready = IO.select([socket, @pipe_r])

      if ready[0].include?(@pipe_r)
        data_to_send = @pipe_r.read_nonblock(1024)
        socket.write(data_to_send)
      end

      if ready[0].include?(socket)
        data_received = socket.read_nonblock(1024)
        h2 << data_received
        break if socket.nil? || socket.closed? || socket.eof?
      end
    end
  end
end

这很漂亮,仅在正常的TCPSocket上有效(如示例所示).我需要改用OpenSSL::SSL::SSLSocket,但是按照 IO.select文档:

This works beautifully, but only with a normal TCPSocket as per the example. I need to use an OpenSSL::SSL::SSLSocket instead, however as per the IO.select docs:

使用IO.select的最佳方法是在非阻塞方法(例如read_nonblock,write_nonblock等)之后调用它.

The best way to use IO.select is invoking it after nonblocking methods such as read_nonblock, write_nonblock, etc.

[...]

尤其是,非阻塞方法和IO.select的组合是IO之类的对象(例如OpenSSL :: SSL :: SSLSocket)的首选.

Especially, the combination of nonblocking methods and IO.select is preferred for IO like objects such as OpenSSL::SSL::SSLSocket.

因此,我需要在非阻塞方法之后调用IO.select ,而在循环中,我会在之前使用它,以便可以从2个不同的IO对象中进行选择.

According to this, I need to call IO.select after nonblocking methods, while in my loop I use it before so I can select from 2 different IO objects.

关于如何将IO.select与SSL套接字一起使用的给定示例为:

The given example on how to use IO.select with an SSL socket is:

begin
  result = socket.read_nonblock(1024)
rescue IO::WaitReadable
  IO.select([socket])
  retry
rescue IO::WaitWritable
  IO.select(nil, [socket])
  retry
end

但是,只有将IO.select单个 IO对象一起使用时,此方法才有效.

However this works only if IO.select is used with a single IO object.

我的问题是:鉴于我需要同时从@pipe_rsocket对象中进行选择,如何使上一个示例与SSL套接字配合使用?

My question is: how can I make my previous example work with an SSL socket, given that I need to select from both the @pipe_r and the socket objects?

我尝试了@ steffen-ullrich的建议,但无济于事.我可以使用以下命令使测试通过:

I've tried what @steffen-ullrich suggested, however to no avail. I was able to make my tests pass using the following:

loop do

  begin
    data_to_send = @pipe_r.read_nonblock(1024)
    socket.write(data_to_send)
  rescue IO::WaitReadable, IO::WaitWritable
  end

  begin
    data_received = socket.read_nonblock(1024)
    h2 << data_received
    break if socket.nil? || socket.closed? || socket.eof?

  rescue IO::WaitReadable
    IO.select([socket, @pipe_r])

  rescue IO::WaitWritable
    IO.select([@pipe_r], [socket])

  end
end

看起来还不错,但是欢迎任何输入.

This doesn't look so bad, but any input is welcome.

推荐答案

我对ruby本身并不熟悉,但是对于将select与基于SSL的套接字一起使用存在问题. SSL套接字的行为与TCP套接字不同,这是因为SSL数据是按帧而不是作为数据流传输的,但是流语义仍被应用于套接字接口.

I'm not familiar with ruby itself, but with the problems of using select with SSL based sockets. SSL sockets behave differently to TCP sockets since SSL data are transferred in frames and not as a data stream, but nevertheless stream semantics are applied to the socket interface.

让我们通过一个示例来说明这一点,首先使用简单的TCP连接:

Let's explain this with an example, first using a simple TCP connection:

  • 服务器一次写入发送1000个字节.
  • 数据将被传递到客户端并放入内核套接字缓冲区中.因此select将返回可用数据.
  • 客户端应用程序仅读取100个字节.
  • 然后将其他900个字节保留在内核套接字缓冲区中.下次调用select将再次返回该数据可用,因为select会查看内核内套接字缓冲区.

对于SSL,这是不同的:

With SSL this is different:

  • 服务器一次写入发送1000个字节.然后,SSL堆栈会将这1000个字节加密为单个SSL帧,然后将此帧发送给客户端.
  • 此SSL帧将被传递到客户端,并放入内核套接字缓冲区中.因此select将返回可用数据.
  • 现在,客户端应用程序仅读取100个字节.由于SSL帧包含1000个字节,并且由于它需要完整的帧来解密数据,因此SSL堆栈将从套接字读取完整的帧,而在内核套接字缓冲区中不保留任何内容.然后它将解密该帧,并将请求的100个字节返回给应用程序.其余解密后的900字节将保留在用户空间的SSL堆栈中.
  • 由于内核套接字缓冲区为空,因此下一次调用select不会返回可用数据.因此,如果应用程序仅处理select,则不会再有未读取的数据,即SSL堆栈中保留了900个字节.

如何处理这种差异:

  • 一种方法是始终尝试读取至少32768个数据,因为这是SSL帧的最大大小.这样,可以确保没有数据仍保留在SSL堆栈中(读取的SSL不会在SSL帧边界上读取).
  • 另一种方法是在调用select之前检查SSL堆栈中是否已解密的数据.仅当没有数据保留在SSL堆栈中时,才应调用select.要检查此类待处理数据",请使用待处理方法.
  • 尝试从非阻塞套接字读取更多数据,直到没有更多数据可用为止.这样,您可以确保SSL堆栈中仍没有待处理的数据.但是请注意,这也会在基础TCP套接字上进行读取,因此与其他套接字(仅在成功选择后才读取)上的数据相比,SSL套接字上的数据可能更受欢迎.这是您引用的建议,但我会推荐其他人.

这篇关于如何在Ruby中创建双向SSL套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆