From: Joel VanderWerf Date: 2008-03-16T04:53:36+09:00 Subject: Re: How to make net-ping thread safe? Laurent Julliard wrote: > Hi, > > I'm using the excellent net-ping gem (v 1.2.2 on Ruby 1.8.6 Linux box) > from Daniel Berger to ping a number of machines on a local area network. > > I recently refactored my code to use threads and I'm having troubles > with Net::Ping::ICMP. I'm trying to ping several machines, each one in a > separate thread and I'm seing strange things like when one machine is on > all three machines are reported on (although two are off). When I run my > program with one thread only the on/off state shows as expected. > > So I'm wondering if the Net::Ping::ICMP#ping method is thread safe? > Daniel said he made no attempt to make sure that the code is and > usggested that I post this question here. > > The code for the method Net::Ping::ICMP#ping is here: > http://www.koders.com/ruby/fidE6256BC790B8AD197544CE26287B5C7D3200E4C8.aspx?s=icmp#L5 > > > (line 61) > > Here is also a simplified version of my own code: > > ----------------------------------------------- > > require 'rubygems' > require 'net/ping/icmp' > > WORK_STATIONS = ['192.168.1.1', '192.168.1.2', '192.168.1.3'] > threads = [] > ping_objects = [] > > WORK_STATIONS.each do |ip| > ping_objects << Net::Ping::ICMP.new(ip) > threads << Thread.new(ip, ping_objects.last) do |ip, p| > puts "Monitoring #{ip}..." > loop do > puts "#{ip} is #{p.ping ? 'on' : 'off'}" > sleep 2 > end > end > end > > threads.each { |th| th.join } > > ------------------------------------------------ > > Any advice either to change my own script or improve the net-ping code > is welcome. > > Thanks for your help! > > Laurent > In icmp.rb, the problem seems to be that it uses process ID as the ICMP packet ID, so it is impossible to tell which ECHO REPLY packet corresponds to which request (if multiple requests come from one process). Quoting http://tools.ietf.org/html/rfc792: The identifier and sequence number may be used by the echo sender to aid in matching the replies with the requests. For example, the identifier might be used like a port in TCP or UDP to identify a session, and the sequence number might be incremented on each request sent. The destination returns these same values in the reply. Maybe it is standard practice to use pid, so that multiple ICMP clients (e.g. /bin/ping) never step on each other. In that case, I guess icmp.rb could fork{sleep} for each Ping instance and use the child pid as a conflict-free identifier.[1] However, watching tcpdump as /bin/ping is running on linux, it seems that the ID might be a local port rather than process id. Maybe ping is just binding a socket to 0 to ask the OS for a unique port. Ruby's icmp should do whatever /bin/ping does. You could work around this by filtering the response by sender host, *if* you assume that different threads never ping the same host. Or you could just shell out to ping (which is what I always do). [1] http://www.ping127001.com/pingpage/ping.html uses PID. -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407