Sto costruendo una libreria Ruby client che si connette a un server e attende i dati, ma consente anche agli utenti di inviare dati chiamando un metodo.Come creare un socket SSL bidirezionale in Ruby
Il meccanismo che uso è quello di avere una classe che inizializza una coppia di socket, in questo modo:
def initialize
@pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0)
end
Il metodo che ho consentono agli sviluppatori di chiamare per inviare i dati al server è simile al seguente:
def send(data)
@pipe_w.write(data)
@pipe_w.flush
end
Poi ho un ciclo in un thread separato, dove seleziono da un socket
collegata al server e dal @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
Questo funziona magnificamente, ma solo con un normale TCPSocket
come nell'esempio. Ho bisogno di utilizzare un OpenSSL::SSL::SSLSocket
invece, tuttavia secondo the IO.select docs:
Il modo migliore per utilizzare IO.select invoca dopo metodi non bloccante, come read_nonblock, write_nonblock, ecc
[...]
In particolare, la combinazione di metodi non bloccanti e IO.select è preferibile per oggetti di tipo IO come OpenSSL :: SSL :: SSLSocket.
In base a questo, ho bisogno di chiamare IO.select
dopo metodi non bloccante, mentre nel mio ciclo lo uso prima in modo da poter scegliere tra 2 diversi oggetti IO.
La proposta esempio su come utilizzare IO.select
con una presa SSL è:
begin
result = socket.read_nonblock(1024)
rescue IO::WaitReadable
IO.select([socket])
retry
rescue IO::WaitWritable
IO.select(nil, [socket])
retry
end
Tuttavia questo funziona solo se IO.select
viene utilizzato con un solo IO oggetto .
La mia domanda è: come posso far funzionare il mio esempio precedente con un socket SSL, dato che ho bisogno di scegliere tra gli oggetti @pipe_r
e socket
?
EDIT: Ho provato ciò che suggeriva @ steffen-ullrich, ma senza successo. Sono stato in grado di fare i miei test passano utilizzando la seguente:
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
questo non sembra così male, ma qualsiasi input è il benvenuto.
Grazie @ steffen-ullrich, questa è una risposta molto chiara. Purtroppo ho provato a utilizzare il metodo in sospeso prima di usare select e ottengo sempre che ci siano 0 byte disponibili. Ho anche provato nonblock_read (32768) e anche questo non è il trucco. Proverò ancora un po '. – ostinelli
Accetterà questa risposta perché chiarisce il modo corretto per risolvere questi problemi. Grazie ancora! – ostinelli