Reputation: 3245
In a Ruby script I'm having a problem with socket connections. What I am doing is the following:
Basically I am kind of acting as a bridge between the 2 servers.
Code looks like this:
require 'uri'
require 'net/http'
require 'json'
@connection1 = Net::HTTP.start 'server1.com'
@connection2 = Net::HTTP.start 'server2.com'
# reads data from server 1 as it comes and sends it to server 2
Thread.new{
while JSON.parse(@connection1.post('/receive').body) !nil
@connection2.post '/send', JSON.parse(@connection1.post('/receive').body)
end
}
# reads data from server 2 as it comes and sends it to server 2
while JSON.parse(@connection2.post('/receive').body) !nil
@connection1.post '/send', JSON.parse(@connection2.post('/receive').body)
end
# Thread.join
# not actually needed because the two connections are supposed to continuously stream data
However as soon as one of the two connections receives data and tries sending it to the other connection I'm receiving the following error:
Socket operation on non-socket - Errno::ENOTSOCK
More in deep stack trace:
C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:176:in wait_readable': socket operation on non-socket. (Errno::ENOTSOCK) from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:176:in 'rbuf_fill' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:154:in 'readuntil' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:164:in 'readline' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http/response.rb:40:in 'read_status_line' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http/response.rb:29:in 'read_new' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1446:in block in 'transport_request' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1443:in 'catch' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1443:in 'transport_request' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1416:in 'request' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1430:in 'send_entity' from C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1218:in 'post'
So what do you think I am doing wrong?
I should add that for reasons beyond my control the two remote servers are configured to serve data when contacted with a POST rather than with a GET.
Upvotes: 1
Views: 1188
Reputation: 1160
You lack any sort of synchronization between both threads and Net::HTTP
is not thread-safe.
What's possibly happening here is that you call @connection1.post /receive
in one thread, that said thread gets paused and the second thread tries to use @connection1.post /send
while connection1
is still being used.
Another problem is that your code in inefficient, you issue two /receive
requests per thread to get information.
while JSON.parse(@connection1.post('/receive').body) !nil
@connection2.post '/send', JSON.parse(@connection1.post('/receive').body)
end
This makes three requests total
Could be
while True
result = JSON.parse(@connection1.post('/receive').body)
break if result.nil?
@connection2.post '/send', result)
end
This makes two requests total
Use a Mutex
to make sure that while connection1
is sending/receiving a request, no other thread touches it.
require 'uri'
require 'net/http'
require 'json'
@connection1 = Net::HTTP.start 'server1.com'
@connection2 = Net::HTTP.start 'server2.com'
connection_1_lock = Mutex.new
connection_2_lock = Mutex.new
# reads data from server 1 as it comes and sends it to server 2
Thread.new do
while True
receive_result = nil
connection_1_lock.synchronize do
receive_result = JSON.parse(@connection1.post('/receive').body)
end
connection_2_lock.synchronize do
@connection2.post '/send', receive_result
end
end
end
Thread.new do
while True
receive_result = nil
connection_2_lock.synchronize do
receive_result = JSON.parse(@connection2.post('/receive').body)
end
connection_1_lock.synchronize do
@connection1.post '/send', receive_result
end
end
end
I believe the code above should fix your problem, although I cannot guarantee it. Concurrent programming is hard.
I suggest you read up on concurrent/multithreaded programming and its pitfalls. There are numerous Ruby resources online.
Since Ruby's documentation on Mutex
is notoriously bad, I'll shamelessly plug my own article here and suggest you read it:
https://dev.to/enether/working-with-multithreaded-ruby-part-i-cj3 (The 'How To Protect Yourself' paragraph introduces mutexes)
Upvotes: 3