在 Ruby 脚本中,我遇到了套接字连接问题。 我正在做的是以下内容:
- 我有两个线程,每个线程创建一个到不同网络服务器的连接
- 每当线程 1 从服务器 1 接收数据时,我希望线程 1 将此数据发布到服务器 2
- 每当线程 2 从服务器 2 接收数据时,我希望线程 2 将此数据发布到服务器 1
基本上我充当了两个服务器之间的桥梁。
代码如下所示:
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
然而,一旦两个连接之一接收到数据并尝试将其发送到另一个连接,我就会收到以下错误:
非套接字上的套接字操作 - Errno::ENOTSOCK
更多深度堆栈跟踪:
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'
那么你认为我做错了什么?
我应该补充一点,出于我无法控制的原因,两个远程服务器被配置为在与 POST 而不是 GET 联系时提供数据。
最佳答案
核心问题
两个线程之间缺少任何类型的同步,Net::HTTP
不是线程安全的。
这里可能发生的情况是,您在一个线程中调用 @connection1.post/receive
,该线程暂停,第二个线程尝试使用 @connection1.post/send
而 connection1
仍在使用中。
另一个问题是您的代码效率低下,每个线程发出两个/receive
请求以获取信息。
while JSON.parse(@connection1.post('/receive').body) !nil
@connection2.post '/send', JSON.parse(@connection1.post('/receive').body)
end
这使得总共三个请求
可能是
while True
result = JSON.parse(@connection1.post('/receive').body)
break if result.nil?
@connection2.post '/send', result)
end
这使得总共有两个请求
建议的解决方案
使用 Mutex
确保当 connection1
正在发送/接收请求时,没有其他线程接触它。
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
我相信上面的代码应该可以解决您的问题,但我不能保证。并发编程很难。
进一步阅读:
我建议您阅读并发/多线程编程及其陷阱。网上有很多 Ruby 资源。
由于 Ruby 关于 Mutex
的文档是出了名的糟糕,我会厚颜无耻地在此处插入我自己的文章并建议您阅读它:
https://dev.to/enether/working-with-multithreaded-ruby-part-i-cj3 (“如何保护自己”段落介绍了互斥量)
关于ruby - 2套接字互操作产生 "socket operation on non-socket - ENOTSOCK"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46750391/