2010-01-08 15 views
13

सॉकेट को गिरा दिया गया है या नहीं, यह पता लगाने का सबसे उचित तरीका क्या है? या क्या वास्तव में एक पैकेट भेजा गया था?जावा सॉकेट और ड्रॉप किए गए कनेक्शन

मेरे पास ऐप्पल गेटवे (available on GitHub) के माध्यम से iPhones में ऐप्पल पुश अधिसूचनाएं भेजने के लिए एक लाइब्रेरी है। ग्राहकों को सॉकेट खोलने और प्रत्येक संदेश का बाइनरी प्रतिनिधित्व भेजने की आवश्यकता होती है; लेकिन दुर्भाग्य से ऐप्पल किसी भी पावती को वापस नहीं लौटाता है। कई संदेशों को भेजने के लिए कनेक्शन का पुन: उपयोग किया जा सकता है। मैं सरल जावा सॉकेट कनेक्शन का उपयोग कर रहा हूँ। प्रासंगिक कोड है:

Socket socket = socket(); // returns an reused open socket, or a new one 
socket.getOutputStream().write(m.marshall()); 
socket.getOutputStream().flush(); 
logger.debug("Message \"{}\" sent", m); 

कुछ मामलों में, यदि कोई संदेश भेजा गया है या ठीक पहले एक कनेक्शन गिरा दिया गया है; Socket.getOutputStream().write() हालांकि सफलतापूर्वक समाप्त होता है। मुझे उम्मीद है कि टीसीपी विंडो अभी तक समाप्त नहीं हुई है।

क्या कोई तरीका है कि मैं यह सुनिश्चित करने के लिए कह सकता हूं कि वास्तव में नेटवर्क में एक पैकेट मिला है या नहीं? मैं निम्नलिखित दो समाधान के साथ प्रयोग:

  1. एक 250ms समय समाप्ति के साथ एक अतिरिक्त socket.getInputStream().read() आपरेशन डालें। यह एक पठन ऑपरेशन को मजबूर करता है जो कनेक्शन को छोड़ने में विफल रहता है, लेकिन अन्यथा 250ms के लिए लटकता है।

  2. संदेश बाइनरी आकार में टीसीपी भेजने बफर आकार (उदा। Socket.setSendBufferSize()) सेट करें।

दोनों विधियां काम करती हैं, लेकिन वे सेवा की गुणवत्ता में काफी कमी करते हैं; थ्रूपुट 100 संदेश/सेकेंड से लगभग 10 संदेश/सेकेंड तक जाता है।

कोई सुझाव?

अद्यतन:

वर्णित की संभावना पर सवाल एकाधिक जवाब द्वारा चुनौती दी। मैंने जिस व्यवहार का वर्णन कर रहा हूं उसके "यूनिट" परीक्षणों का निर्माण किया। यूनिट के मामलों को Gist 273786 पर देखें।

दोनों इकाई परीक्षणों में दो धागे, एक सर्वर और ग्राहक हैं। सर्वर बंद हो जाता है जबकि क्लाइंट किसी भी IOException के बिना डेटा भेज रहा है।

public static void main(String[] args) throws Throwable { 
    final int PORT = 8005; 
    final int FIRST_BUF_SIZE = 5; 

    final Throwable[] errors = new Throwable[1]; 
    final Semaphore serverClosing = new Semaphore(0); 
    final Semaphore messageFlushed = new Semaphore(0); 

    class ServerThread extends Thread { 
     public void run() { 
      try { 
       ServerSocket ssocket = new ServerSocket(PORT); 
       Socket socket = ssocket.accept(); 
       InputStream s = socket.getInputStream(); 
       s.read(new byte[FIRST_BUF_SIZE]); 

       messageFlushed.acquire(); 

       socket.close(); 
       ssocket.close(); 
       System.out.println("Closed socket"); 

       serverClosing.release(); 
      } catch (Throwable e) { 
       errors[0] = e; 
      } 
     } 
    } 

    class ClientThread extends Thread { 
     public void run() { 
      try { 
       Socket socket = new Socket("localhost", PORT); 
       OutputStream st = socket.getOutputStream(); 
       st.write(new byte[FIRST_BUF_SIZE]); 
       st.flush(); 

       messageFlushed.release(); 
       serverClosing.acquire(1); 

       System.out.println("writing new packets"); 

       // sending more packets while server already 
       // closed connection 
       st.write(32); 
       st.flush(); 
       st.close(); 

       System.out.println("Sent"); 
      } catch (Throwable e) { 
       errors[0] = e; 
      } 
     } 
    } 

    Thread thread1 = new ServerThread(); 
    Thread thread2 = new ClientThread(); 

    thread1.start(); 
    thread2.start(); 

    thread1.join(); 
    thread2.join(); 

    if (errors[0] != null) 
     throw errors[0]; 
    System.out.println("Run without any errors"); 
} 

[संयोग से, मैं भी एक संगामिति परीक्षण पुस्तकालय, सेटअप करता है कि एक सा बेहतर और स्पष्ट है: यहाँ मुख्य विधि है। गिस्ट पर भी नमूना चेकआउट करें]।

जब चलाने मैं निम्नलिखित उत्पादन प्राप्त करें:

+0

अद्यतन उत्तर। –

+0

जैसा कि मैंने पहले ही अपने उत्तर में कहा है, यदि आप यह सुनिश्चित करना चाहते हैं कि कोई त्रुटि नहीं है तो आपको एक सुंदर कनेक्शन समाप्ति करना होगा। शटडाउन आउटपुट() को निष्पादित करें, तब तक जब तक आप ईओएफ प्राप्त नहीं करते हैं, तब सॉकेट बंद करें। सुप्रसिद्ध कनेक्शन समाप्ति संक्षेप में सहकर्मी से एक स्वीकृति प्राप्त करने में है कि यह आपको ठीक है, जो वही है जो आप चाहते हैं। –

उत्तर

17

यह आप के लिए ज्यादा मदद नहीं हो सकता है, लेकिन तकनीकी रूप से अपने प्रस्तावित समाधान के दोनों सही नहीं हैं। OutputStream.flush() और जो भी एपीआई कॉल आप सोच सकते हैं वह आपको करने की ज़रूरत नहीं है।

सहकर्मी द्वारा एक पैकेट प्राप्त किया गया है या नहीं, यह निर्धारित करने के लिए एकमात्र पोर्टेबल और विश्वसनीय तरीका है कि सहकर्मी से पुष्टि की प्रतीक्षा करें। यह पुष्टि या तो वास्तविक प्रतिक्रिया हो सकती है, या एक सुंदर सॉकेट शटडाउन हो सकता है। कहानी का अंत - वास्तव में कोई अन्य तरीका नहीं है, और यह जावा विशिष्ट नहीं है - यह मौलिक नेटवर्क प्रोग्रामिंग है।

यदि यह लगातार कनेक्शन नहीं है - यानी, यदि आप केवल कुछ भेजते हैं और फिर कनेक्शन बंद करते हैं - जिस तरह से आप इसे करते हैं, तो आप सभी IOExceptions को पकड़ते हैं (उनमें से कोई भी त्रुटि इंगित करता है) और आप एक सुंदर सॉकेट करते हैं शट डाउन:

1. socket.shutdownOutput(); 
2. wait for inputStream.read() to return -1, indicating the peer has also shutdown its socket 
+1

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

2

यदि आप सेब में टीसीपी/आईपी प्रोटोकॉल का उपयोग करके जानकारी भेज रहे हैं तो आपको पावती प्राप्त करनी होगी। लेकिन अगर आप ने कहा:

एप्पल वापस नहीं करता है किसी भी पावती

जो भी आप इस से क्या मतलब है? टीसीपी/आईपी गारंटी वितरण इसलिए रिसीवर रसीद को स्वीकार करना चाहिए। यह गारंटी नहीं देता है कि वितरण कब होगा।

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

@ टिप्पणी स्पष्टीकरण/प्रश्न

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

क्या ऐसी स्थिति है जहां डेटा प्रतीत होता है "भेजा गया" लेकिन प्राप्त होने की गारंटी नहीं है जहां कोई अपवाद नहीं उठाया जाता है? जवाब नहीं होना चाहिए।

@Examples

अच्छा उदाहरण है, इस बातें काफ़ी स्पष्ट करता है। मैंने सोचा होगा कि एक त्रुटि फेंक दी जाएगी। उदाहरण में पोस्ट किया गया दूसरा त्रुटि पर एक त्रुटि फेंक दी गई है, लेकिन पहले नहीं। यह दिलचस्प व्यवहार है ... और मैं यह समझाने में ज्यादा जानकारी नहीं पा रहा था कि यह ऐसा क्यों व्यवहार करता है। हालांकि यह बताता है कि वितरण को सत्यापित करने के लिए हमें अपने स्वयं के आवेदन स्तर प्रोटोकॉल क्यों विकसित करना चाहिए।

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

http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/

लगता है की तरह जवाब नहीं क्या आप वाकई के लिए बता सकते हैं या नहीं, पर है। आप वायरसहार्क जैसे पैकेट स्निफर का उपयोग करने के लिए कह सकते हैं कि यह भेजा गया है या नहीं, लेकिन यह अभी भी गारंटी नहीं देगा कि यह प्राप्त किया गया था और सेवा की प्रकृति के कारण डिवाइस पर भेजा गया था।

+0

मेरा मतलब है कि ऐप्पल टीसीपी एसीके से परे कोई विशिष्ट प्रोटोकॉल पावती वापस नहीं करता है। मुझे यह भी नहीं पता कि मैं कैसे बता सकता हूं कि कौन से पैकेट ठीक से भेजे गए थे और कौन से नहीं थे। – notnoop

+0

अद्यतन के लिए धन्यवाद। मुझे एहसास है कि मुझे ऐप्पल सेवाओं पर चर्चा करने के बजाय, उदाहरण के साथ टीसीपी पर सवाल पर ध्यान देना चाहिए था। मुझे लगता है कि मैं अब के लिए गिराए गए कनेक्शन पहचान समर्थन के लिए छोड़ दूंगा। – notnoop

3

गिरा कनेक्शन के साथ ज्यादा मुसीबत के बाद, मैं use the enhanced format करने के लिए अपने कोड ले जाया गया है, जो काफी मतलब है कि आप इस तरह देखने के लिए अपने पैकेज बदलने के लिए:

enter image description here

इस तरह एप्पल एक अगर एक कनेक्शन ड्रॉप नहीं होगा त्रुटि होती है, लेकिन सॉकेट को एक फीडबैक कोड लिखेंगे।

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