2个套接字互操作产生“非套接字上的套接字操作 - ENOTSOCK”错误

时间:2017-10-14 23:48:06

标签: ruby multithreading sockets http

在Ruby脚本中,我遇到套接字连接问题。 我正在做的是以下内容:

  • 我有两个线程,每个线程创建一个到不同Web服务器的连接
  • 任何时候线程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:在   wait_readable':非套接字上的套接字操作。 (错误:: ENOTSOCK)           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:176:在'rbuf_fill'中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:154:在'readuntil'中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/protocol.rb:164:在'readline'中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http/response.rb:40:in   'read_status_line'           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http/response.rb:29:in"read_new'           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1446:在'transport_request'中的块中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1443:'catch'           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1443:在'transport_request'中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1416:在'request'中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1430:在'send_entity'中           来自C:/Dev/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:1218:在'post'

那么您认为我做错了什么?

我应该补充一点,由于我无法控制的原因,两个远程服务器配置为在与POST联系而不是GET时提供数据。

1 个答案:

答案 0 :(得分:3)

核心问题

两个线程之间缺少任何类型的同步,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(“如何保护自己”一节介绍了互斥体)