我正在构建一个客户端 Ruby 库,它连接到服务器并等待数据,但也允许用户通过调用方法来发送数据。
我使用的机制是有一个初始化套接字对的类,如下所示:
def initialize
@pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0)
end
我允许开发人员调用以向服务器发送数据的方法如下所示:
def send(data)
@pipe_w.write(data)
@pipe_w.flush
end
然后我在一个单独的线程中有一个循环,我从socket
连接到服务器的一个和从@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 文档:
使用 IO.select 的最佳方式是在 read_nonblock、write_nonblock 等非阻塞方法之后调用它。
[...]
尤其是对于像 OpenSSL::SSL::SSLSocket 这样的 IO 类对象,非阻塞方法和 IO.select 的组合是首选。
据此,我需要在非阻塞方法IO.select
之后调用,而在我的循环中我之前使用它,所以我可以从 2 个不同的 IO 对象中进行选择。
有关如何使用IO.select
SSL 套接字的给定示例是:
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 对象一起使用时才有效。
我的问题是:鉴于我需要从对象@pipe_r
和socket
对象中进行选择,我如何才能使我之前的示例与 SSL 套接字一起使用?
编辑:我尝试了@steffen-ullrich 的建议,但无济于事。我能够使用以下方法通过测试:
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
这看起来还不错,但欢迎任何意见。