我正在建立一个基于Ruby SSLSocket
的自定义客户端。为了接收数据,我一直在使用OpenSSL::Buffering
模块提供的read
和read_nonblock
方法。
我现在正试图采取我目前所做的,并使其能够定义一个回调(通过用户定义的块),该回调将在收到消息时运行。看起来我基本上需要单独实现这些内容:
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
),我应该有什么顾虑?
答案 0 :(得分:1)
我建议两种方法来实现它。
1)使用Kernel.select
或IO.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(全局解释器锁)之外,这使得库非常快。
我一直在生产中使用它并且效果很好!
这两种方法都可以按照您的要求进行操作,因此我建议对它们进行基准测试,看看哪种方法最符合您的需求。