prevent “No buffer space available” error in ruby

2019-09-09 10:29发布

问题:

I have a small script which scans all the ips ranging from 192.168.190.xxx to 192.168.220.xxx on port 411.

The script works fine sometimes, but sometimes I get the error "No buffer space available" dcport.rb:8:ininitialize': No buffer space available - connect(2) (Errno::ENOBUFS)`

I have read that this occurs when the socket were not closed properly, but I have used mysocket.close to prevent that which I suppose does not work properly.

How to prevent this from happening, I mean how to close the socket properly?

My code is as follows

require 'socket'
require 'timeout'
(190...216).each do |i|
  (0...255).each do |j|
    begin
      #puts "Scanning 192.168.#{i}.#{j}"
      scan=Timeout::timeout(10/1000.0) {
        s=TCPSocket.new("192.168.#{i}.#{j}",411)
        s.close
        puts "192.168.#{i}.#{j} => Hub running"
      }
    rescue Timeout::Error
    rescue Errno::ENETUNREACH
    rescue Errno::ECONNREFUSED
    end
  end
end

回答1:

My guess is that, sometimes, the timeout fires between the socket creation and the socket closing, which makes you leak some sockets. Since (as far as a quick google search told me), ENOBUFS happens by default after 1024 sockets opened, that could definitely be it.

Timeout, as well as Thread.raise, is very harmful in situations where you need to be sure that something happens (in your case, s.close), as you actually cannot guarantee it anymore: the exception could be raised anywhere, even within an ensure block.

In your case, I think that you could fix it by adding an ensure clause outside the timeout block (untested code follows):

require 'socket'
require 'timeout'
(190...216).each do |i|
  (0...255).each do |j|
    begin
      #puts "Scanning 192.168.#{i}.#{j}"
      s = nil
      scan=Timeout::timeout(10/1000.0) do
        s=TCPSocket.new("192.168.#{i}.#{j}",411)
        puts "192.168.#{i}.#{j} => Hub running"
      end
    rescue Timeout::Error
    rescue Errno::ENETUNREACH
    rescue Errno::ECONNREFUSED
    ensure
      s.close if s
    end
  end
end