Je construis une bibliothèque client Ruby qui se connecte à un serveur et attend des données, mais permet également aux utilisateurs d'envoyer des données en appelant une méthode.Comment puis-je créer un socket SSL bidirectionnel dans Ruby
Le mécanisme que j'utilise est d'avoir une classe qui initialise une paire de socket, comme ceci:
def initialize
@pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0)
end
La méthode que je permettre aux développeurs d'appeler pour envoyer des données au serveur ressemble à ceci:
def send(data)
@pipe_w.write(data)
@pipe_w.flush
end
Ensuite, j'ai une boucle dans un fil séparé, où je sélectionne à partir d'un socket
connecté au serveur et à partir du @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
Cela fonctionne magnifiquement, mais seulement avec un TCPSocket
normal selon l'exemple. Je dois utiliser un lieu OpenSSL::SSL::SSLSocket
, mais comme par the IO.select docs:
La meilleure façon d'utiliser IO.select invoque après des méthodes bloquantes telles que read_nonblock, write_nonblock, etc.
[...]
En particulier, la combinaison de méthodes non bloquantes et d'IO.select est préférée pour les objets de type IO tels que OpenSSL :: SSL :: SSLSocket.
Selon ce, je dois appeler IO.select
après méthodes bloquantes, alors que dans ma boucle, je l'utilise avant je peux choisir parmi 2 différents objets IO.
L'exemple donné sur la façon d'utiliser IO.select
avec une prise SSL est:
begin
result = socket.read_nonblock(1024)
rescue IO::WaitReadable
IO.select([socket])
retry
rescue IO::WaitWritable
IO.select(nil, [socket])
retry
end
Cependant, cela ne fonctionne que si IO.select
est utilisé avec un seul objet IO.
Ma question est: comment puis-je faire fonctionner mon exemple précédent avec un socket SSL, étant donné que je dois sélectionner à la fois les objets @pipe_r
et socket
?
EDIT: J'ai essayé ce que @ steffen-ullrich suggéré, mais en vain. J'ai été en mesure de faire passer mes tests en utilisant ce qui suit:
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
Cela ne semble pas si mal, mais toute entrée est la bienvenue.
Merci @ steffen-ullrich, ceci est une réponse très claire. Malheureusement, j'ai essayé d'utiliser la méthode en attente avant d'utiliser select et j'obtiens toujours qu'il y a 0 octets disponibles. J'ai également essayé nonblock_read (32768) et cela ne fait pas l'affaire non plus. J'essaierai un peu plus. – ostinelli
Acceptera cette réponse car elle clarifie la bonne façon de résoudre ces problèmes. Merci encore! – ostinelli