जावा सॉकेट एपीआई: यदि कनेक्शन बंद कर दिया गया है तो कैसे बताएं?


92

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

isConnected()एक सॉकेट पर कॉल करना जो दूर से डिस्कनेक्ट हो गया है, हमेशा वापस लौटने लगता है true। इसी तरह, isClosed()एक सॉकेट पर कॉल करना जो दूर से बंद हो गया है, हमेशा वापस लौटने लगता है false। मैंने पढ़ा है कि वास्तव में यह निर्धारित करना है कि सॉकेट बंद किया गया है या नहीं, डेटा आउटपुट स्ट्रीम को लिखा जाना चाहिए और एक अपवाद को पकड़ा जाना चाहिए। यह इस स्थिति को संभालने के लिए एक बहुत अशुद्ध तरीका लगता है। हमें बस लगातार नेटवर्क पर एक कचरा संदेश को स्पैम करना होगा जब एक सॉकेट बंद हो गया था।

क्या कोई और उपाय है?

जवाबों:


183

कोई टीसीपी एपीआई नहीं है जो आपको कनेक्शन की वर्तमान स्थिति बताएगा। isConnected()और isClosed()आपको अपने सॉकेट की वर्तमान स्थिति बताता है । एक ही बात नहीं है।

  1. isConnected()आपको बताता है कि क्या आपने इस सॉकेट को कनेक्ट किया है । आपके पास है, इसलिए यह सच है।

  2. isClosed()आपको बताता है कि क्या आपने इस सॉकेट को बंद कर दिया है। जब तक आपके पास है, तब तक यह गलत है।

  3. अगर सहकर्मी ने कनेक्शन को व्यवस्थित तरीके से बंद कर दिया है

    • read() रिटर्न -1
    • readLine() रिटर्न null
    • readXXX()EOFExceptionकिसी अन्य XXX के लिए फेंकता है ।

    • एक लेखन फेंक देगा IOException: 'कनेक्शन रीसेट पीयर द्वारा', अंततः, बफरिंग देरी के अधीन।

  4. यदि कनेक्शन किसी अन्य कारण से गिरा है, तो एक लेखन एक IOException, अंततः, जैसा कि ऊपर लिखा है, और एक ही काम करेगा।

  5. यदि सहकर्मी अभी भी जुड़ा हुआ है, लेकिन कनेक्शन का उपयोग नहीं कर रहा है, तो रीड टाइमआउट का उपयोग किया जा सकता है।

  6. इसके विपरीत जो आप कहीं और पढ़ सकते हैं, ClosedChannelExceptionवह आपको यह नहीं बताता। [न ही करता है SocketException: socket closed.] यह केवल आपको बताता है कि आपने चैनल बंद कर दिया है , और फिर इसका उपयोग करना जारी रखा है। दूसरे शब्दों में, आपके हिस्से पर एक प्रोग्रामिंग त्रुटि। यह एक बंद कनेक्शन को इंगित नहीं करता है

  7. Windows XP पर Java 7 के साथ कुछ प्रयोगों के परिणामस्वरूप यह भी प्रतीत होता है कि यदि:

    • आप चुन रहे हैं OP_READ
    • select() शून्य से अधिक का मान लौटाता है
    • संबद्ध SelectionKeyपहले से अमान्य है ( key.isValid() == false)

    इसका मतलब है कि सहकर्मी ने कनेक्शन को रीसेट कर दिया है। हालाँकि, यह JRE संस्करण या प्लेटफ़ॉर्म के लिए अजीब हो सकता है।


18
यह मानना ​​मुश्किल है कि टीसीपी प्रोटोकॉल, जो कनेक्शन उन्मुख है, इसके कनेक्शन की स्थिति भी नहीं जान सकता है ... क्या इस प्रोटोकॉल के साथ आने वाले लोग अपनी कार को बंद आँखों से चलाते हैं?
पेडरोड

31
@PedroD इसके विपरीत: यह जानबूझकर किया गया था। पिछले प्रोटोकॉल जैसे SNA में 'डायल टोन' था। टीसीपी को एक परमाणु युद्ध से बचने के लिए डिज़ाइन किया गया था, और अधिक तुच्छता से, राउटर डाउन और अप: इसलिए डायल टोन, कनेक्शन स्थिति, आदि जैसी किसी भी चीज़ की पूर्ण अनुपस्थिति; और यह भी है कि आरएफसी में टीसीपी की निगरानी को विवादास्पद विशेषता के रूप में वर्णित किया गया है, और यह हमेशा डिफ़ॉल्ट रूप से बंद क्यों है। टीसीपी अभी भी हमारे साथ है। SNA? IPX? आईएसओ? नहीं। उन्होंने इसे सही पाया।
लोर्न

3
मुझे विश्वास नहीं है कि यह जानकारी हमसे छिपाने के लिए एक अच्छा बहाना है। यह जानते हुए कि कनेक्शन खो गया था जरूरी नहीं है कि प्रोटोकॉल कम दोष प्रतिरोधी है, यह हमेशा इस बात पर निर्भर करता है कि हम उस ज्ञान के साथ क्या करते हैं ... मेरे लिए यह विधि शुद्ध है और जावा से अलग है शुद्ध नकली तरीके हैं, उनका कोई उपयोग नहीं है , लेकिन एक कनेक्शन ईवेंट श्रोता की आवश्यकता व्यक्त करते हैं ... लेकिन मैं दोहराता हूं: यह जानते हुए कि कनेक्शन खो गया था, प्रोटोकॉल को बदतर नहीं बनाता है। अब अगर वे प्रोटोकॉल कह रहे हैं कि जैसे ही उन्हें पता चला कि यह खो गया है, तो एक अलग कहानी है।
13

21
@Pedro आपको समझ नहीं आ रहा है। यह एक 'बहाना' नहीं है। रोक लगाने की कोई सूचना नहीं है। कोई डायल टोन नहीं है। टीसीपी को पता नहीं है कि क्या कनेक्शन विफल हो गया है जब तक कि आप इसके लिए कुछ करने की कोशिश नहीं करते हैं। यह मौलिक डिजाइन मानदंड था।
लोरेन का मार्कीटी

2
@EJP आपके उत्तर के लिए धन्यवाद। क्या आप शायद जानते हैं कि "ब्लॉकिंग" के बिना एक सॉकेट बंद हो गया है तो कैसे जांचें? रीड या रीडलाइन का उपयोग ब्लॉक करने जा रहा है। क्या यह महसूस करने का एक तरीका है कि क्या उपलब्ध विधि के साथ अन्य सॉकेट बंद हो गया है, इसके 0बजाय वापस लौटना प्रतीत होता है -1
insumity

9

विभिन्न मैसेजिंग प्रोटोकॉल में एक-दूसरे को दिल खोलकर रखना (पिंग पैकेट भेजते रहना) आम बात है, पैकेट को बहुत बड़ा होने की जरूरत नहीं है। जांच तंत्र आपको सामान्य रूप से टीसीपी के आंकड़ों से पहले ही डिस्कनेक्ट किए गए क्लाइंट का पता लगाने की अनुमति देगा (टीसीपी टाइमआउट कहीं अधिक है) एक जांच भेजें और उत्तर के लिए 5 सेकंड के लिए प्रतीक्षा करें, यदि आप कहने के लिए उत्तर नहीं देखते हैं तो 2-3 बाद में जांच, आपका खिलाड़ी काट दिया गया है।

साथ ही, संबंधित प्रश्न


6
यह कुछ प्रोटोकॉल में 'सामान्य अभ्यास' है । मैं ध्यान देता हूं कि ग्रह पर सबसे अधिक उपयोग किया जाने वाला HTTP प्रोटोकॉल, पिंग ऑपरेशन नहीं है।
लोर्ने

2
@ user207421 क्योंकि HTTP / 1 एक शॉट प्रोटोकॉल है। आप अनुरोध भेजते हैं, आपको प्रतिक्रिया मिलती है। और आप कर रहे हैं। सॉकेट बंद है। Websocket एक्सटेंशन में PING ऑपरेशन है, इसलिए HTTP / 2 करता है।
माइकल ज़ैबेल्स्की

मैंने एक ही बाइट के साथ अगर संभव हो तो दिल की धड़कन की सिफारिश की :)
स्टीफन रीच

@ MichałZabielski ऐसा इसलिए है क्योंकि HTTP के डिजाइनरों ने इसे निर्दिष्ट नहीं किया था। तुम्हें पता नहीं क्यों नहीं। HTTP / 1.1 एक शॉट प्रोटोकॉल नहीं है। सॉकेट बंद नहीं है: HTTP कनेक्शन डिफ़ॉल्ट रूप से लगातार हैं। FTP, SMTP, POP3, IMAP, TLS, ... दिल की धड़कन नहीं है।
लोरेन की मार्किस

2

मैं सिर्फ पोस्ट किए गए दूसरे उत्तर को देखता हूं, लेकिन मुझे लगता है कि आप अपने गेम को खेलने वाले ग्राहकों के साथ इंटरएक्टिव हैं, इसलिए मैं एक और दृष्टिकोण रख सकता हूं (जबकि कुछ मामलों में बफेडरेडर निश्चित रूप से वैध है)।

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

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

यदि आपके जावा कोड ने सॉकेट को बंद / डिस्कनेक्ट नहीं किया है, तो आपको और कैसे सूचित किया जाएगा कि दूरस्थ होस्ट ने आपका कनेक्शन बंद कर दिया है? अंततः, आपकी कोशिश / पकड़ लगभग एक ही काम कर रही है कि एक मतदाता ACTUAL सॉकेट पर घटनाओं के लिए सुन रहा होगा। निम्नलिखित को धयान मे रखते हुए:

  • आपका स्थानीय सिस्टम आपको सूचित किए बिना आपके सॉकेट को बंद कर सकता है ... यह सिर्फ सॉकेट का कार्यान्वयन है (यानी यह राज्य परिवर्तन के लिए हार्डवेयर / ड्राइवर / फर्मवेयर / जो कुछ भी प्रदूषित नहीं करता है)।
  • नए सॉकेट (प्रॉक्सी पी) ... कई पार्टियां हैं (वास्तव में 6 समापन बिंदु) जो आप पर कनेक्शन बंद कर सकते हैं ...

मुझे लगता है कि अमूर्त भाषाओं की एक विशेषता यह है कि आप माइनुटिया से अमूर्त हैं। SqlConnection s या जो कुछ भी ... के लिए C # (कोशिश / अंत में) में कीवर्ड का उपयोग करने के बारे में सोचें ... यह सिर्फ व्यापार करने की लागत है ... मुझे लगता है कि कोशिश / पकड़ / अंत में सॉकेट के उपयोग के लिए स्वीकार किए जाते हैं और परिशिष्ट पैटर्न है।


आपका स्थानीय सिस्टम आपको सूचित किए बिना या उसके साथ 'सॉकेट को बंद' नहीं कर सकता है। आपका प्रश्न 'यदि आपका जावा कोड सॉकेट बंद / बंद नहीं करता ...'? कोई मतलब नहीं है।
लोरेन का मार्किस

1
सॉकेट को बंद करने से सिस्टम (उदाहरण के लिए लिनक्स) को वास्तव में क्या रोकता है? क्या 'कॉल क्लोज़' कमांड का उपयोग करके 'gdb' से इसे करना अभी भी संभव है?
एंड्री लेब्डेंको 17

@AndreyLebedenko कुछ भी नहीं 'वास्तव में इसे रोकता है', लेकिन यह ऐसा नहीं करता है।
लोर्ने का मार्किस

@EJB फिर कौन करता है?
एंड्री लेब्डेंको

@AndreyLebedenko कोई नहीं करता है। केवल एप्लिकेशन अपने स्वयं के सॉकेट्स को बंद कर सकता है, जब तक कि वह ऐसा किए बिना बाहर निकल जाता है, जिस स्थिति में ओएस साफ हो जाएगा।
लोर्ने की

1

मुझे लगता है कि यह tcp कनेक्शन की प्रकृति है, इससे पहले कि हम यह निष्कर्ष निकाल लें कि मानकों में संचरण में लगभग 6 मिनट का मौन है! इसलिए मुझे नहीं लगता कि आप इस समस्या का सटीक समाधान पा सकते हैं। शायद बेहतर तरीका यह है कि जब उपयोगकर्ता को उपयोगकर्ता कनेक्शन बंद करना चाहिए तो अनुमान लगाने के लिए कुछ आसान कोड लिखें।


2
इसमें दो घंटे तक बहुत अधिक लग सकते हैं।
लोरेन के मार्कीस

0

जैसा कि @ user207421 का कहना है कि टीसीपी / आईपी प्रोटोकॉल आर्किटेक्चर मॉडल के कारण कनेक्शन की वर्तमान स्थिति को जानने का कोई तरीका नहीं है। इसलिए सर्वर को कनेक्शन बंद करने से पहले आपको नोटिस करना होगा या आप खुद से इसकी जांच करेंगे।
यह एक सरल उदाहरण है जो दिखाता है कि सर्वर द्वारा सॉकेट को कैसे बंद किया जाता है:

sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT);
socket = new Socket();
timeout = 5000;
socket.connect(sockAdr, timeout);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream());
while ((data = reader.readLine())!=null) 
      log.e(TAG, "received -> " + data);
log.e(TAG, "Socket closed !");

0

मुझे इसी तरह की समस्या का सामना करना पड़ा। मेरे मामले में क्लाइंट को समय-समय पर डेटा भेजना चाहिए। मुझे उम्मीद है कि आपकी भी यही आवश्यकता होगी। फिर मैंने SO_TIMEOUT सेट किया socket.setSoTimeout(1000 * 60 * 5);जो कि java.net.SocketTimeoutExceptionनिर्दिष्ट समय समाप्त होने पर फेंक दिया जाता है। फिर मैं मृत ग्राहक का आसानी से पता लगा सकता हूं।


-2

मैं इसे कैसे संभालता हूं

 while(true) {
        if((receiveMessage = receiveRead.readLine()) != null ) {  

        System.out.println("first message same :"+receiveMessage);
        System.out.println(receiveMessage);      

        }
        else if(receiveRead.readLine()==null)
        {

        System.out.println("Client has disconected: "+sock.isClosed()); 
        System.exit(1);
         }    } 

अगर result.code == अशक्त


आपको कॉल करने की आवश्यकता नहीं है readLine() दो बार । आप पहले से ही जानते हैं कि यह elseब्लॉक में शून्य था ।
लोर्न

-4

लिनक्स पर जब लिखते हैं () एक सॉकेट में प्रवेश करता है, जो दूसरे पक्ष, आपके लिए अज्ञात है, एक SIGPIPE सिग्नल / अपवाद को भड़काने देगा, हालांकि आप इसे कॉल करना चाहते हैं। हालाँकि अगर आप SIGPIPE द्वारा पकड़े नहीं जाना चाहते हैं तो आप ध्वज MSG_NOSIGNAL के साथ सेंड () का उपयोग कर सकते हैं। भेजें () कॉल -1 के साथ वापस आ जाएगी और इस मामले में आप इरनो की जांच कर सकते हैं, जो आपको बताएगा कि आपने EPIPE के साथ एक टूटे हुए पाइप (इस मामले में सॉकेट) लिखने की कोशिश की है, जो ग़लती के अनुसार है। 32. EPIPE की प्रतिक्रिया के रूप में आप डबल बैक कर सकते हैं और सॉकेट को फिर से खोलने और अपनी जानकारी फिर से भेजने का प्रयास कर सकते हैं।


send()कॉल वापस आ जाएगी -1 केवल तभी बाहर जाने वाले डेटा भेजने टाइमर समाप्त होने तक काफी देर तक के लिए बफ़र कर ली। यह लगभग निश्चित रूप से डिस्कनेक्ट के बाद पहले भेजने पर नहीं होगा, दोनों छोरों पर बफरिंग के कारण और send()हुड के नीचे की अतुल्यकालिक प्रकृति ।
लोर्ने के मार्किस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.