1

我正在构建一个基于 Ruby 的自定义客户端SSLSocket。为了接收数据,我一直在使用模块提供的readread_nonblock方法。OpenSSL::Buffering

我现在正在尝试采用我目前所拥有的东西,并使其能够定义一个回调(通过用户定义的块),该回调将在收到消息时运行。看起来我基本上需要单独实现这些行:

thread = Thread.new do
  while !socket.closed?
    while (data = socket.read_nonblock(1024) rescue nil)
      @buffer << data
    end

    sleep 0.1

    # ... parse full messages from @buffer & deliver to callbacks ...
  end
end

thread.run

我对这种方法的问题是它不是真正的事件驱动的,并且由于数据实际可用,可能会有长达 100 毫秒的延迟。当然,我可以改变睡眠时间,但它只是感觉有点骇人听闻。

我可以为此使用更好的方法吗?如果没有,我应该决定实施更短/更快的循环(例如:),我应该有什么顾虑sleep 0.01

4

1 回答 1

1

我建议两种方法来实现它。

1)使用Kernel.selectorIO.select方法(两者相同):

require 'socket'
require 'openssl'

s     = TCPSocket.new(host, prot) 
ssl   = OpenSSL::SSL::SSLSocket.new(s)
ssl.connect

t = Thread.new do
  loop do
    sr, sw = IO.select [ssl]
    puts sr.first.readline
    puts '...'
  end
end

puts 'start reading'
t.join # join the main thread

IO.select等待直到一些数据到达,没有繁忙的循环。这个解决方案的好处是,它只使用标准的 Ruby 库。

2)使用EventMachine库:

require 'eventmachine'

module Client

  def post_init
    start_tls
  end

  def receive_data data
    # include callback code
    puts data
    puts '...'
  end

end

EM.run do 
  # start event loop
  EM.connect 'localhost', 9000, Client
end

EventMachine,根据文档,是使用 Reactor 模式的事件驱动 I/O。

开箱即用,EventMachine拥有您所需的一切。反应器是用 C++ 实现的,线程模型在 Ruby GIL(全局解释器锁)之外,这使得库非常快。

我已经在生产中使用它一段时间了,效果很好!

这两种方法都可以按照您的要求工作,因此我建议对它们进行基准测试,看看哪一种最适合您的需求。

于 2013-11-12T17:07:54.213 回答