2009-02-05 35 views
38

Tôi cần một cách nhanh chóng để tìm hiểu xem một cổng đã cho có được mở bằng Ruby hay không. Tôi hiện đang quan trọng xung quanh với điều này:Ruby - Kiểm tra xem cổng có đang mở

require 'socket' 

def is_port_open?(ip, port) 
    begin 
    TCPSocket.new(ip, port) 
    rescue Errno::ECONNREFUSED 
    return false 
    end 
    return true 
end 

Nó hoạt động tuyệt vời nếu các cổng đang mở, nhưng nhược điểm của việc này là thỉnh thoảng nó sẽ chỉ ngồi và chờ 10-20 giây và sau đó cuối cùng thời gian ra, ném một ngoại lệ ETIMEOUT (nếu cổng bị đóng). Do đó, câu hỏi của tôi là:

Mã này có thể được sửa đổi để chỉ chờ một giây (và trả lại false nếu chúng tôi không nhận được gì lúc đó) hay không? ?

Chỉnh sửa: Mã bash gọi có thể chấp nhận được miễn là nó hoạt động trên nhiều nền tảng (ví dụ: Mac OS X, * nix và Cygwin), mặc dù tôi thích mã Ruby hơn.

Trả lời

43

giống như sau có thể làm việc:

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 
+0

trình như một say mê! Cảm ơn! –

+0

Tôi gặp sự cố với việc chặn này (tôi nghĩ). Về cơ bản, thời gian chờ sẽ không thực sự hết thời gian. Không chắc chắn tại sao, nhưng giải pháp netcat hoạt động tốt ở vị trí của nó. –

+2

Câu trả lời này có một giải pháp cũng hoạt động trên các cửa sổ: http://stackoverflow.com/a/3473208/362951 – mit

10

Chỉ cần cho đầy đủ, các Bash sẽ là một cái gì đó như thế này:

$ netcat $HOST $PORT -w 1 -q 0 </dev/null && do_something 

-w 1 quy định cụ thể một thời gian chờ 1 giây, và -q 0 nói rằng, khi được kết nối, hãy đóng kết nối ngay sau khi stdin cung cấp cho EOF (trong đó /dev/null sẽ hoạt động ngay lập tức).

Bash cũng có các dịch vụ/UDP built-in TCP riêng của mình, nhưng họ là một lựa chọn thời gian biên dịch và tôi không có một Bash biên soạn với họ: P

+1

Chúng khá đơn giản: chỉ giả vờ/dev/{tcp}/HOST/PORT là các tập tin :) – ephemient

+2

Để tham khảo trong tương lai, tôi thấy điều này là 'nc' trên hệ thống của tôi thay vì' netcat' – HXCaine

+1

Cảnh báo: Trên MacOS X, điều này cho biết lỗi 'nc: invalid option - q'. Các công trình sau đây trên cả MacOS X và Ubuntu, và có vẻ đơn giản hơn với tôi: 'nc -z $ HOST $ PORT' – mercurial

26

More của Ruby cú pháp ngữ:

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 
+0

Điều này đã cho kết quả dương tính giả cho các đầu vào '192.0.2.0', 80, 10 sẽ không hợp lệ (theo http://stackoverflow.com/questions/10456044/what-is-a-good-invalid-ip-address-to-use-for-unit-tests). Tôi nhận được kết quả tương tự với Ruby 1.9.3p448 và 2.0.0p195, cả trên Mac. Trong trường hợp nào phương pháp này quản lý để trả về false? (Tôi thậm chí đã cố gắng viết vào ổ cắm trước khi đóng nó, nhưng điều đó vẫn trở lại đúng!) –

+0

Chỉ cần thử '0.0.0.0', 80, 1 và điều này cũng đã đúng. –

+0

hoạt động tốt cho tôi! – sunsations

1

Biến thể nhỏ của tôi đối với câu trả lời của Chris Rice. Vẫn xử lý thời gian ra trên một nỗ lực duy nhất nhưng cũng cho phép nhiều lần thử lại cho đến khi bạn bỏ cuộc.

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 
9

Gần đây tôi đã đưa ra giải pháp này, việc sử dụng các lệnh unix lsof:

def port_open?(port) 
    !system("lsof -i:#{port}", out: '/dev/null') 
end 
+2

Điều này rất tốt cho tôi. Tôi muốn giới thiệu một hệ thống gán các cổng cho các máy ảo trong không trung thực và đã viết một lớp lót này để kiểm tra xem cổng tôi sắp gán có được mở hay không: 'vms ['port'] + = 1 trong khi cổng. bao gồm? vms ['port'] hoặc hệ thống ("lsof -i: # {vms ['port']}") ' – Dannid

+1

Điều này chỉ hoạt động đối với người dùng đã đăng nhập. Để làm việc trên bảng, sử dụng 'sudo lsof -i: ' – alpinweis

+0

Tôi đã phải xóa '!' (Không phải toán tử) để làm việc này hoạt động. –

7

Tất cả các câu trả lời khác hiện không được ưa chuộng. Sử dụng Timeoutdiscouraged. Có lẽ mọi thứ phụ thuộc vào phiên bản ruby. Ít nhất kể từ 2.0 một cách đơn giản có thể sử dụng:

Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {} 

Đối cũ ruby ​​phương pháp tốt nhất tôi có thể tìm được sử dụng chế độ non-blocking và sau đó select. Mô tả ở đây:

+3

Làm việc hoàn hảo cho tôi: 'port_is_open = Socket.tcp (máy chủ, cổng, connect_timeout: 5) {true} rescue false'. Thật dễ dàng để mở rộng từ một lớp lót để giải cứu các trường hợp ngoại lệ cụ thể cần thiết. – anothermh

2

Tất cả * nix nền tảng:

thử nc/netcat lệnh như sau.

`nc -z -w #{timeout_in_seconds} -G #{timeout_in_seconds} #{host} #{port}` 
if $?.exitstatus == 0 
    #port is open 
else 
    #refused, port is closed 
end 

Cờ -z có thể được sử dụng để báo cho nc báo cáo cổng mở, thay vì bắt đầu kết nối.

Cờ -w nghĩa Timeout cho kết nối và ròng thức đọc

Cờ -G là thời gian chờ kết nối trong vài giây

Sử dụng -n cờ để làm việc với địa chỉ IP chứ không phải là tên máy.

Ví dụ:

# `nc -z -w 1 -G 1 google.com 80` 
# `nc -z -w 1 -G 1 -n 123.234.1.18 80` 
Các vấn đề liên quan