2009-05-07 25 views
6

Tôi có một yêu cầu hơi kỳ lạ để có thể nghe một số giao diện mạng từ Java trên máy Linux và xác định xem một trong số chúng có nhận các gói UDP của loại nhất định. Dữ liệu đầu ra mà tôi cần là địa chỉ IP của giao diện được đề cập. Có cách nào để làm điều này trong Java?Java trên Linux: Nghe các tin nhắn phát sóng trên một địa chỉ cục bộ bị ràng buộc

Nghe địa chỉ ký tự đại diện (new DatagramSocket (port)) không hỗ trợ bởi vì trong khi tôi nhận được các gói tin phát sóng, tôi không thể xác định địa chỉ IP cục bộ của giao diện mà chúng đi qua. Lắng nghe các chương trình phát sóng trong khi bị ràng buộc với một giao diện nhất định (DatagramSocket mới (cổng, địa chỉ)) không nhận được các gói tin nào cả. Trường hợp này xứng đáng là một mã ví dụ trong đó cho thấy những gì tôi đang cố gắng để làm:

Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); 
while (interfaces.hasMoreElements()) { 
    NetworkInterface ni = (NetworkInterface) interfaces.nextElement(); 
    Enumeration addresses = ni.getInetAddresses(); 
    while (addresses.hasMoreElements()) { 
    InetAddress address = (InetAddress)addresses.nextElement(); 
    if (address.isLoopbackAddress() || address instanceof Inet6Address) 
     continue; //Not interested in loopback or ipv6 this time, thanks 
    DatagramSocket socket = new DatagramSocket(PORT, address); 
    //Try to read the broadcast messages from socket here 
    } 
} 

Tôi cũng đã cố gắng để khởi tạo các ổ cắm với địa chỉ quảng bá xây dựng dựa trên phần đầu của IP thực của giao diện và phần còn lại theo đúng mặt nạ mạng:

byte [] mask = { (byte)255, 0, 0, 0 }; 
byte[] addrBytes = InetAddress.getByName("126.5.6.7").getAddress(); 
for (int i=0; i < 4; i++) { 
    addrBytes[i] |= ((byte)0xFF)^mask[i]; 
} 
InetAddress bcastAddr = InetAddress.getByAddress(addrBytes); 

Điều đó chỉ ném BindException khi xây dựng DatagramSocket.

EDIT: BindException (java.net.BindException: Không thể gán địa chỉ yêu cầu) từ gọi constructor DatagramSocket với một phát sóng địa chỉ (ví dụ 126.255.255.255) chỉ đi kèm với các mới nhất Ubuntu 9,04 (có lẽ không Ubuntu, nhưng hạt nhân vấn đề cụ thể -version). Với Ubuntu 8.10, công việc này cũng như với bản phát hành Red Hat (RHEL 4.x), tôi đang giải quyết.

Dường như không nhận được các gói trong khi được liên kết với một IP cục bộ nhất định là correct behaviour, mặc dù trong cửa sổ hoạt động. Tôi cần làm cho nó hoạt động trên Linux (RHEL và Ubuntu). Với mã C cấp thấp có một bộ giải mã (SO_BINDTODEVICE) mà tôi không thể tìm thấy trong các API Java. This không chính xác làm cho tôi bật với sự lạc quan mặc dù :-)

+0

Dường như lỗi đó chưa được sửa trong 10 năm tới !! Khùng! : D –

Trả lời

4

Đây là vấn đề hạt nhân Linux IPV6 cuối cùng. Thông thường tôi đã vô hiệu hóa IPV6, bởi vì nó gây ra tất cả các loại nhức đầu. Tuy nhiên trong Ubuntu 9,04 nó là như vậy khó khăn để vô hiệu hóa IPV6 mà tôi đã từ bỏ, và rằng bit tôi.

Để nghe phát sóng thông điệp từ một giao diện nào đó, đầu tiên tôi sẽ tạo ra các "phiên bản truyền hình" của địa chỉ IP của giao diện:

byte [] mask = { (byte)255, 0, 0, 0 }; 
byte[] addrBytes = InetAddress.getByName("126.5.6.7").getAddress(); 
for (int i=0; i < 4; i++) { 
    addrBytes[i] |= ((byte)0xFF)^mask[i]; 
} 
InetAddress bcastAddr = InetAddress.getByAddress(addrBytes); 

Cấp, điều này không thực sự ràng buộc tôi đến một giao diện nhất định nếu nhiều giao diện có một IP bắt đầu với cùng một phần mạng, nhưng đối với tôi giải pháp này là đủ.

Sau đó, tôi tạo datagramsocket với địa chỉ đó (và cổng mong muốn), và nó hoạt động. Nhưng không phải không chuyển các thuộc tính hệ thống sau đây cho JVM:

-Djava.net.preferIPv6Addresses=false -Djava.net.preferIPv4Stack=true 

Tôi không biết IPV6 cố gắng phá vỡ các chương trình phát sóng như thế nào, và các tham số trên khắc phục nó.

0

Không chắc chắn nếu điều này giúp nhưng tôi biết rằng để có được một danh sách tất cả các giao diện mạng:

Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); 

lẽ bạn có thể liên kết với nhau một cách độc lập?

Chỉ tìm thấy một số ví dụ tuyệt vời về cách sử dụng getNetworkInterfaces().

+0

Lặp qua giao diện/địa chỉ là chính xác những gì tôi đang làm, nhưng vấn đề là tôi không thể nghe các tin nhắn quảng bá. Tôi không nhận được bất cứ điều gì với bất kỳ địa chỉ, chỉ khi tôi tạo DatagramSocket với constructor với tham số cổng đồng bằng (sử dụng "địa chỉ wildcard"). – auramo

0

Để theo sự hiểu biết của tôi cách duy nhất để làm được điều này sẽ có tùy chọn ổ cắm

IP_RECVDSTADDR

. Tùy chọn này được yêu cầu để đảm bảo rằng địa chỉ dst của giao diện mà gói tin đến có sẵn khi được kết nối với địa chỉ đại diện. Vì vậy, nó nên làm việc với phát sóng quá tôi giả định.

Dưới đây là một ví dụ C Tôi nhặt ra khỏi Internet:

How to get UDP destination address on incoming packets

tôi sẽ đọc lên trên recvmsg và sau đó cố gắng và tìm hiểu xem giao diện này hiện có sẵn trong Java.

Edit:

Tôi chỉ nhận ra rằng bạn có thể có một lựa chọn hơn nếu nó được hỗ trợ trong Java. Bạn vẫn có thể cần tùy chọn ổ cắm IP_RECVDSTADDR (không chắc chắn), nhưng thay vì sử dụng recvmsg, bạn có thể sử dụng ổ cắm thô và lấy địa chỉ đích từ tiêu đề IP.

Mở ổ cắm của bạn bằng cách sử dụng SOCK_RAW và bạn sẽ nhận được tiêu đề IP đầy đủ ở đầu mỗi thư bao gồm địa chỉ nguồn và đích.

Dưới đây là một ví dụ của việc sử dụng UDP với một ổ cắm nguyên trong C trên Linux:

Advanced TCP/IP - THE RAW SOCKET PROGRAM EXAMPLES

Tôi sẽ ngạc nhiên nếu phương pháp này không làm việc trong Java cũng có.

Edit2

hơn Một ý tưởng. Có lý do nào khiến bạn không thể sử dụng Multicast hoặc một lý do cụ thể mà bạn đã chọn Broadcast over Multicast? Theo như tôi hiểu với Multicast, bạn luôn biết Giao diện nào nhận được gói vì bạn luôn liên kết với một giao diện cụ thể khi tham gia nhóm Multicast (đặc biệt là với IP4 nơi bạn liên kết với giao diện thông qua một trong các địa chỉ IP của nó).

+0

Dường như nó có thể kết thúc trong Java 7 một ngày nào đó. – auramo

+0

Tôi vừa nhận ra rằng bạn có thể có thêm một tùy chọn nếu nó được hỗ trợ trong Java. Hãy xem bản chỉnh sửa của tôi. –

+0

Sử dụng ổ cắm thô từ Java sẽ yêu cầu mã nguồn gốc và các cuộc gọi JNI hoặc JNA. Tôi không muốn đến đó, bởi vì nó khá rắc rối. Tôi thà rơi trở lại kế hoạch của tôi B mà là để cấu hình tĩnh giao diện để sử dụng. Nó có vấn đề về khả năng sử dụng, nhưng nó đánh bại tùy chọn mã gốc. – auramo

1

Để giải quyết vấn đề của bạn, bạn cần phải xác định xem gói UDP phát sóng giao diện nào đã được nhận.

  • Nếu bạn liên kết với địa chỉ ký tự đại diện bạn nhận được chương trình phát sóng, nhưng không có cách nào để xác định địa chỉ mạng nhận được gói.
  • Nếu bạn liên kết với một giao diện cụ thể, bạn biết địa chỉ giao diện nào bạn đang nhận, nhưng không còn nhận được các chương trình phát sóng (ít nhất là trên ngăn xếp TCP/IP của Linux).

Như những người khác đã đề cập, có thư viện ổ cắm nguyên bên thứ ba cho Java chẳng hạn như RockSaw hoặc Jpcap, có thể giúp bạn xác định địa chỉ của giao diện thực tế.

+0

Cảm ơn, tôi sẽ kiểm tra các thư viện đó và xem chúng di động như thế nào. Những gì tôi muốn là chỉ cần sao chép các tập tin nhị phân giữa win <-> unix mà không phải lo lắng. – auramo

0

Không thể nhận xét, vì vậy hãy thêm câu trả lời này làm câu trả lời thay thế.

Điều đó thật thú vị. Mặc dù tò mò tại sao bạn làm

byte[] addrBytes = InetAddress.getByName("126.5.6.7").getAddress(); 

thay vì chỉ

byte[] addrBytes = {126, 5, 6, 7); 

hoặc là nó rằng các địa chỉ giao diện được với bạn như String?

+0

Không có lý do gì: bạn đúng mà chỉ liệt kê các byte trong một mảng là thanh lịch hơn. – auramo

Các vấn đề liên quan