2009-05-07 12 views
6

मेरे पास कुछ हद तक अजीब आवश्यकता है कि जावा से कई नेटवर्क इंटरफेस को लिनक्स मशीन पर सुन सकें और यह निर्धारित करें कि उनमें से एक को यूडीपी पैकेट प्राप्त होता है या नहीं खास प्रकार का। आउटपुट डेटा जो मुझे चाहिए, इंटरफ़ेस का आईपी पता प्रश्न में है। जावा में ऐसा करने का कोई तरीका है?लिनक्स पर जावा: एक बाध्य स्थानीय पते पर संदेशों को प्रसारित करने के लिए सुनना

वाइल्डकार्ड पता (नया डेटाग्राम सॉकेट (पोर्ट)) पर सुनना मदद नहीं करता है क्योंकि मुझे ब्रॉडकास्ट पैकेट मिलने पर, मैं इंटरफ़ेस के स्थानीय आईपी पते को निर्धारित नहीं कर सकता। किसी निश्चित इंटरफ़ेस (नए डेटाग्राम सॉकेट (पोर्ट, पता) से बाध्य होने पर प्रसारण को सुनना) को पैकेट प्राप्त नहीं होते हैं। इस मामले में एक कोड उदाहरण के हकदार है जो दिखाता है कि मैं क्या करने की कोशिश कर रहा हूँ:

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 
    } 
} 

मैं भी इंटरफ़ेस का असली आईपी और बाकी की शुरुआत के आधार पर निर्माण किया प्रसारण पते के साथ सॉकेट प्रारंभ करने की कोशिश की सही नेटमास्क के अनुसार:

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); 

यह डेटाग्राम सॉकेट बनाने के दौरान बस एक बाइंड अपवाद फेंकता है।

संपादित करें: BindException (java.net.BindException: अनुरोध पता निर्दिष्ट नहीं कर सकते) एक प्रसारण-पता (126.255.255.255 जैसे) के साथ DatagramSocket के निर्माता को कॉल करने से केवल नवीनतम Ubuntu 9.04 (शायद नहीं उबंटू, लेकिन गिरी के साथ आता है हालांकि अलग-अलग मुद्दे)। उबंटू 8.10 के साथ यह काम करता है, साथ ही साथ Red Hat रिलीज (RHEL 4.x) के साथ मैं काम कर रहा हूं।

जाहिर है कि एक निश्चित स्थानीय आईपी से बंधे हुए पैकेट प्राप्त नहीं कर रहे हैं correct behaviour, हालांकि विंडोज़ में यह काम करता है। मुझे इसे लिनक्स (आरएचईएल और उबंटू) पर काम करने की ज़रूरत है। निम्न-स्तरीय सी-कोड के साथ एक वर्कअराउंड सेटॉकॉप (SO_BINDTODEVICE) है जिसे मैं जावा-एपीआई में नहीं ढूंढ सकता। This बिल्कुल मेरे आशावाद के साथ फट नहीं है, हालांकि :-)

+0

ऐसा लगता है कि उस बग को 10 वर्षों में तय नहीं किया गया है !! पागल! : डी –

उत्तर

4

यह अंत में एक आईपीवी 6 लिनक्स कर्नेल मुद्दा था। आम तौर पर मैंने आईपीवी 6 को अक्षम कर दिया है, क्योंकि यह सभी प्रकार के सिरदर्द का कारण बनता है। हालांकि उबंटू 9.04 में आईपीवी 6 को निष्क्रिय करना इतना मुश्किल है जिसे मैंने छोड़ दिया, और वह मुझे थोड़ा सा।

एक निश्चित इंटरफ़ेस से संदेश प्रसारित करने के लिए सुनने के लिए, मैं पहली बार इंटरफेस के आईपी पते की "प्रसारण संस्करण" बना देंगे:

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); 

दी, यह वास्तव में एक निश्चित इंटरफ़ेस करने के लिए मुझे बाध्य नहीं करता है यदि कई इंटरफेस में एक आईपी है जो एक ही नेटवर्क भाग से शुरू होता है, लेकिन मेरे लिए यह समाधान पर्याप्त है।

फिर मैं उस पते (और वांछित बंदरगाह) के साथ डेटाग्रामसेट बना देता हूं, और यह काम करता है। लेकिन नहीं JVM करने के लिए निम्नलिखित प्रणाली गुण गुजर बिना:

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

मुझे पता नहीं कैसे IPV6 प्रसारण को सुनने को तोड़ने में सफल हुआ है, लेकिन यह करता है, और इसके बाद के संस्करण पैरामीटर लगा दें।

0

सुनिश्चित नहीं हैं कि अगर यह मदद करता है लेकिन मैं जानता हूँ कि सभी नेटवर्क इंटरफेस की एक सूची प्राप्त करने के लिए:

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

हो सकता है कि आप प्रत्येक के लिए बाध्य कर सकते हैं एक स्वतंत्र रूप से?

बस GetNetworkInterfaces() के उपयोग पर कुछ शानदार उदाहरण पाए गए।

+0

इंटरफेस/पतों के माध्यम से लूपिंग ठीक है जो मैं कर रहा हूं, लेकिन समस्या यह है कि मैं प्रसारण संदेशों को नहीं सुन सकता। मुझे किसी भी पते के साथ कुछ भी नहीं मिलता है, केवल तभी जब मैं सादा बंदरगाह पैरामीटर ("वाइल्डकार्ड पता" का उपयोग करके) के साथ निर्माता के साथ डेटाग्राम सॉकेट बनाता हूं। – auramo

0

मेरी जानकारी के अनुसार करने के लिए यह करने के लिए

IP_RECVDSTADDR

सॉकेट विकल्प के साथ होगा एक ही रास्ता। यह विकल्प यह सुनिश्चित करना है कि वाइल्डकार्ड पते से बाध्य होने पर इंटरफ़ेस का डीएसटी पता उपलब्ध हो। तो मुझे लगता है कि प्रसारण के साथ भी काम करना चाहिए।

How to get UDP destination address on incoming packets

मैं recvmsg को पढ़ने और फिर कोशिश करते हैं और पता करें कि इस इंटरफेस जावा में उपलब्ध है जाएगा:

यहाँ एक सी उदाहरण मैं इंटरनेट बंद उठाया है।

संपादित करें:

मैं सिर्फ एहसास हुआ अगर यह जावा में समर्थित है आप एक और विकल्प हो सकता है। आपको अभी भी IP_RECVDSTADDR सॉकेट विकल्प (सुनिश्चित नहीं) की आवश्यकता हो सकती है, लेकिन recvmsg का उपयोग करने के बजाय आप कच्चे सॉकेट का उपयोग कर सकते हैं और आईपी हेडर से गंतव्य पता प्राप्त कर सकते हैं।

SOCK_RAW का उपयोग करने के रूप में अपनी सॉकेट खोलें और आपको स्रोत और गंतव्य पते सहित प्रत्येक संदेश की शुरुआत में पूर्ण आईपी हेडर प्राप्त होगा।

यहाँ लिनक्स पर सी में एक कच्चे सॉकेट के साथ यूडीपी का उपयोग करने का एक उदाहरण है:

Advanced TCP/IP - THE RAW SOCKET PROGRAM EXAMPLES

मैं अगर इस विधि जावा में भी काम नहीं करता है आश्चर्य होगा।

EDIT2

एक और विचार है। क्या मल्टीकास्ट या मल्टीकास्ट पर ब्रॉडकास्ट चुनने के एक विशिष्ट कारण का उपयोग करने का कोई कारण नहीं है? जहां तक ​​मैं मल्टीकास्ट के साथ समझता हूं, आप हमेशा जानते होंगे कि मल्टीकास्ट समूह में शामिल होने पर आप हमेशा एक विशिष्ट इंटरफ़ेस से जुड़ते हैं (विशेष रूप से आईपी 4 के साथ जहां आप इसके आईपी पते में से किसी एक के माध्यम से इंटरफ़ेस से जुड़ते हैं)।

+0

ऐसा लगता है कि यह कुछ दिनों में जावा 7 में समाप्त हो सकता है। – auramo

+0

मुझे अभी एहसास हुआ है कि यदि जावा में समर्थित है तो आपके पास एक और विकल्प हो सकता है। मेरे संपादन पर एक नज़र डालें। –

+0

जावा से कच्चे सॉकेट का उपयोग करने के लिए मूल कोड और जेएनआई या जेएनए कॉल की आवश्यकता होगी। मैं वहां जाना नहीं चाहता, क्योंकि यह काफी परेशानी है। मैं अपनी योजना बी पर वापस आना चाहता हूं जो उपयोग करने के लिए इंटरफ़ेस को स्थिर रूप से कॉन्फ़िगर करना है। इसमें उपयोगिता के मुद्दे हैं, लेकिन यह मूल कोड विकल्प धड़कता है। – auramo

1

अपनी समस्या को पुनरारंभ करने के लिए, आपको यह निर्धारित करने की आवश्यकता है कि कौन सा इंटरफ़ेस प्रसारण यूडीपी पैकेट प्राप्त हुआ था।

  • यदि आप वाइल्डकार्ड पते से जुड़ते हैं तो आपको प्रसारण प्राप्त होते हैं, लेकिन यह निर्धारित करने का कोई तरीका नहीं है कि पैकेट किस नेटवर्क पते पर प्राप्त हुआ था।
  • यदि आप किसी विशिष्ट इंटरफ़ेस से जुड़ते हैं तो आप जानते हैं कि आप किस इंटरफ़ेस पते पर प्राप्त कर रहे हैं, लेकिन अब प्रसारण (कम से कम लिनक्स टीसीपी/आईपी स्टैक पर) प्राप्त नहीं होता है।

दूसरों के रूप में उल्लेख किया है, इस तरह के RockSaw या Jpcap के रूप में जावा के लिए तीसरे पक्ष के कच्चे सॉकेट लाइब्रेरीज, जिसमें आप वास्तविक इंटरफ़ेस का पता लगाने में मदद कर सकते हैं।

+0

धन्यवाद, मैं उन पुस्तकालयों को देखता हूं और देखता हूं कि वे कितने पोर्टेबल हैं। मैं चाहता हूं कि चिंता के बिना <-> यूनिक्स जीतने के बीच द्विआधारी की प्रतिलिपि बनाना है। – auramo

0

टिप्पणी नहीं कर सकता, इसलिए इसे इसके बजाय उत्तर के रूप में जोड़ना।

यह दिलचस्प है। हालांकि उत्सुक हूँ तुम क्यों

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

बजाय

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

या यह है कि इंटरफ़ेस पतों स्ट्रिंग के रूप में आप करने के लिए मिलता है?

+0

कोई कारण नहीं: आप सही हैं कि केवल सरणी में बाइट्स को गिनती करना अधिक सुरुचिपूर्ण है। – auramo

संबंधित मुद्दे