روبي - معرفة ما إذا كان المنفذ مفتوحًا
-
21-08-2019 - |
سؤال
أحتاج إلى طريقة سريعة لمعرفة ما إذا كان منفذ معين مفتوحًا مع روبي.أنا حاليا أعبث بهذا:
require 'socket'
def is_port_open?(ip, port)
begin
TCPSocket.new(ip, port)
rescue Errno::ECONNREFUSED
return false
end
return true
end
إنه يعمل بشكل رائع إذا كان المنفذ مفتوحًا، ولكن الجانب السلبي لذلك هو أنه في بعض الأحيان سيجلس وينتظر لمدة 10-20 ثانية ثم ينتهي الوقت في النهاية، ويرمي ETIMEOUT
استثناء (إذا كان المنفذ مغلقا).سؤالي هو بالتالي:
هل يمكن تعديل هذا الرمز للانتظار لمدة ثانية فقط (والعودة false
إذا لم نحصل على أي شيء بحلول ذلك الوقت) أم أن هناك طريقة أفضل للتحقق مما إذا كان منفذ معين مفتوحًا على مضيف معين؟
يحرر: يعد استدعاء رمز bash مقبولًا أيضًا طالما أنه يعمل عبر الأنظمة الأساسية (على سبيل المثال، Mac OS X و*nix وCygwin)، على الرغم من أنني أفضل رمز Ruby.
المحلول
قد يعمل شيء مثل ما يلي:
require 'socket'
require 'timeout'
def is_port_open?(ip, port)
begin
Timeout::timeout(1) do
begin
s = TCPSocket.new(ip, port)
s.close
return true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
return false
end
end
rescue Timeout::Error
end
return false
end
نصائح أخرى
المزيد من بناء الجملة الاصطلاحية لروبي:
require 'socket'
require 'timeout'
def port_open?(ip, port, seconds=1)
Timeout::timeout(seconds) do
begin
TCPSocket.new(ip, port).close
true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
false
end
end
rescue Timeout::Error
false
end
جميع الإجابات الأخرى الموجودة غير مرغوب فيها.استخدام Timeout
يكون محبط.ربما تعتمد الأمور على إصدار روبي.على الأقل منذ 2.0 يمكن للمرء ببساطة استخدام:
Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {}
بالنسبة للروبي الأقدم، فإن أفضل طريقة يمكن أن أجدها هي استخدام وضع عدم الحظر وبعد ذلك select
.الموصوفة هنا:
لقد توصلت مؤخرًا إلى هذا الحل باستخدام نظام يونكس lsof
يأمر:
def port_open?(port)
!system("lsof -i:#{port}", out: '/dev/null')
end
فقط من أجل الاكتمال، سيكون Bash شيئًا مثل هذا:
$ netcat $HOST $PORT -w 1 -q 0 </dev/null && do_something
-w 1
يحدد مهلة 1 ثانية، و -q 0
يقول أنه عند الاتصال، قم بإغلاق الاتصال في أقرب وقت stdin
يعطي EOF
(أيّ /dev/null
سوف تفعل على الفور).
يحتوي Bash أيضًا على خدمات TCP/UDP المضمنة الخاصة به، لكنها خيار وقت الترجمة وليس لدي Bash مجمعة معها :P
جميع منصات *nix:
جرب الأمر nc/netcat كما يلي.
`nc -z -w #{timeout_in_seconds} -G #{timeout_in_seconds} #{host} #{port}`
if $?.exitstatus == 0
#port is open
else
#refused, port is closed
end
يمكن استخدام العلامة -z لإخبار nc بالإبلاغ عن المنافذ المفتوحة، بدلاً من بدء الاتصال.
تعني العلامة -w مهلة الاتصال وقراءات الشبكة النهائية
تشير العلامة -G إلى انتهاء مهلة الاتصال بالثواني
استخدم علامة -n للعمل مع عنوان IP بدلاً من اسم المضيف.
أمثلة:
# `nc -z -w 1 -G 1 google.com 80`
# `nc -z -w 1 -G 1 -n 123.234.1.18 80`
اختلافي الطفيف في إجابة كريس رايس.لا يزال يتعامل مع المهلة في محاولة واحدة ولكنه يسمح أيضًا بمحاولات متعددة حتى تستسلم.
def is_port_open?(host, port, timeout, sleep_period)
begin
Timeout::timeout(timeout) do
begin
s = TCPSocket.new(host, port)
s.close
return true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
sleep(sleep_period)
retry
end
end
rescue Timeout::Error
return false
end
end