Buduję bibliotekę Ruby klienta, która łączy się z serwerem i czeka na dane, ale także pozwala użytkownikom wysyłać dane przez wywołanie metody.Jak utworzyć dwukierunkowe gniazdo SSL w Ruby
Mechanizm używam jest mieć klasę, która inicjuje parę gniazd, tak:
def initialize
@pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0)
end
metoda, która mi pozwoli deweloperom zadzwonić do wysyłania danych na serwer wygląda następująco:
def send(data)
@pipe_w.write(data)
@pipe_w.flush
end
Wtedy muszę pętlę w oddzielnym wątku, gdzie wybrać z socket
podłączonego do serwera, a z @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
To działa pięknie, , ale tylko z normalnym TCPSocket
, jak na przykładzie. Muszę użyć OpenSSL::SSL::SSLSocket
zamiast jednak zgodnie the IO.select docs:
Najlepszym sposobem korzystania IO.select powołuje go po metodach nonblocking takich jak read_nonblock, write_nonblock itp
[...]
Szczególnie połączenie metod niezablokowania i IO.select jest preferowane dla obiektów typu IO takich jak OpenSSL :: SSL :: SSLSocket.
Zgodnie z tym, muszę zadzwonić IO.select
po metod blokowania, podczas gdy w moim pętli Używam go przed więc mogę wybrać jedną z 2 różnych obiektów IO.
podanym przykładzie, w jaki sposób korzystać z IO.select
z gniazdem SSL jest:
begin
result = socket.read_nonblock(1024)
rescue IO::WaitReadable
IO.select([socket])
retry
rescue IO::WaitWritable
IO.select(nil, [socket])
retry
end
Jednak to działa tylko wtedy, gdy jest używany IO.select
z pojedynczego obiektu IO.
Moje pytanie brzmi: w jaki sposób mogę sprawić, aby mój poprzedni przykład działał z gniazdem SSL, biorąc pod uwagę, że muszę wybrać zarówno z obiektów @pipe_r
i socket
?
EDYCJA: Próbowałem, co sugerował @ steffen-ullrich, jednak bez skutku. Udało mi się przetestować następujące parametry:
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
To nie wygląda tak źle, ale wszelkie dane wejściowe są mile widziane.
Dziękuję @ steffen-ullrich, jest to bardzo jasna odpowiedź. Niestety próbowałem użyć oczekującej metody przed użyciem select i zawsze otrzymuję, że są dostępne 0 bajtów. Próbowałem też nonblock_read (32768) i to też nie działa. Spróbuję trochę więcej. – ostinelli
Przyjmie tę odpowiedź, ponieważ wyjaśnia poprawny sposób rozwiązania tych problemów. Jeszcze raz dziękuję! – ostinelli