سؤال

ما هي الطريقة الأكثر ملاءمة للكشف عن ما إذا كان قد تم إسقاط المقبس أم لا؟ أو ما إذا كانت قد أرسلت حزمة بالفعل؟

لدي مكتبة لإرسال إشعارات دفع Apple إلى iPhone من خلال Apple Gatways (متوفر على Github.). يحتاج العملاء إلى فتح مقبس وإرسال تمثيل ثنائي لكل رسالة؛ ولكن لسوء الحظ أبل لا ترجع أي اعتراف على الإطلاق. يمكن إعادة استخدام الاتصال لإرسال رسائل متعددة أيضا. أنا أستخدم اتصالات جافا المقبس بسيطة. التعليمات البرمجية ذات الصلة هي:

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() التشطيبات بنجاح على الرغم من. أتوقع أنه بسبب نافذة TCP لم تستنفد بعد.

هل هناك طريقة يمكنني أن أخبرها بالتأكيد ما إذا كانت الحزمة في الواقع في الشبكة أم لا؟ جربت الحلول التاليين:

  1. إدراج إضافية socket.getInputStream().read() العملية مع مهلة 250ms. هذه تفرض عملية قراءة تفشل عند إسقاط الاتصال، ولكن معلقة خلاف ذلك مقابل 250 مللي ثانية.

  2. اضبط TCP إرسال حجم المخزن المؤقت (على سبيل المثال 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");
}

بالمناسبة، لدي أيضا مكتبة اختبار التزامن، مما يجعل الإعداد أفضل قليلا أو أكثر وضوحا. الخروج العينة في GIST أيضا].

عند تشغيل أحصل على الإخراج التالي:

Closed socket
writing new packets
Finished writing
Run without any errors
هل كانت مفيدة؟

المحلول

لا يكون هذا الكثير من المساعدة لك، ولكن من الناحية الفنية حلولك المقترحة غير صحيحة. outcovertstream.flush () وأي شيء آخر يدعو API، يمكنك التفكير في عدم القيام بما تحتاج إليه.

الطريقة الوحيدة المحمولة والموثوقة لتحديد ما إذا كان قد تم استلام الحزمة من قبل الأقران هو انتظار التأكيد من النظير. يمكن أن يكون هذا التأكيد إما استجابة فعلية، أو إيقاف تشغيل مقبس رشيق. نهاية القصة - هناك حقا ليست هناك طريقة أخرى، وليس جافا محددة - إنها برمجة شبكة أساسية.

إذا لم يكن هذا اتصالا ثابتا - أي، إذا قمت فقط بإرسال شيء ما، ثم إغلاق الاتصال - الطريقة التي تقوم بها، بالطريقة التي تقوم بها، أنت تصطاد جميع المعلومات IOExpions (أي منهم يشير إلى خطأ) وأنت تقوم بإجراء إيقاف تشغيل مأخذ مني

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

نصائح أخرى

بعد الكثير من المتاعب مع الاتصالات المسقطة، انتقلت التعليمات البرمجية الخاصة بي استخدم التنسيق المحسن, ، والتي تعني إلى حد كبير أن تغير الحزمة الخاصة بك لتبدو مثل هذا:

enter image description here

وبهذه الطريقة لن تسقط Apple اتصالا في حالة حدوث خطأ، ولكنك تكتب رمز ردود الفعل إلى المقبس.

إذا كنت ترسل معلومات باستخدام بروتوكول TCP / IP إلى Apple، يجب أن تتلقى شكر وتقدير. ومع ذلك ذكرت:

أبل لا ترجع أي اعتراف على الإطلاق

ما الذي تعنيه بهذا؟ ضمانات TCP / IP التسليم لذلك يجب على جهاز الاستقبال الاعتراف بإيصاله. لا يضمن عندما يحدث التسليم، ولكن.

إذا قمت بإرسال إشعار إلى Apple وأنت تقطر اتصالك قبل استلام ACK، فلا توجد طريقة لمعرفة ما إذا كنت ناجحا أم لا يجب عليك ببساطة إرسالها مرة أخرى. إذا دفعت نفس المعلومات مرتين مشكلة أو غير معالجتها بشكل صحيح من خلال الجهاز، فهناك مشكلة. الحل هو إصلاح التعامل مع الجهاز لإخطار الدفع المكرر: لا يوجد شيء يمكنك القيام به على جانب الدفع.

توضيح التحميل / السؤال

نعم. الجزء الأول من ما تفهمه هو إجابتك على الجزء الثاني. تم إرسال الحزم فقط التي تلقت acks واستلمتها بشكل صحيح. أنا متأكد من أننا يمكن أن نفكر في بعض مخطط معقد للغاية لتتبع كل حزمة فردية أنفسنا، لكن TCP يفترض أن مجردة هذه الطبقة بعيدا وتتعامل معها لك. في نهايتك، يجب عليك ببساطة التعامل مع العديد من الإخفاقات التي يمكن أن تحدث (في Java إذا حدث أي من هذه الاستثناء). إذا لم يكن هناك استثناء البيانات التي حاولت إرسالها، فسيتم إرسالها مضمونة من قبل بروتوكول TCP / IP.

هل هناك موقف حيث يبدو أن البيانات "مرسلة" ولكن لم يتم ضمان الاستلام حيث لا يتم رفع أي استثناء؟ يجب أن تكون الجواب لا.

@أمثلة

أمثلة لطيفة، وهذا يوضح الأشياء قليلا جدا. كنت أعتقد أنه سيتم طرح خطأ. في المثال نشر خطأ يتم طرحه على الكتابة الثانية، ولكن ليس الأول. هذا سلوك مثير للاهتمام ... ولم أتمكن من العثور على الكثير من المعلومات التي تشرح لماذا يتصرف بهذا الشكل. ومع ذلك، يفسر لماذا يجب علينا تطوير بروتوكولات مستوى التطبيق الخاص بنا للتحقق من التسليم.

يبدو أنك صحيحة أنه بدون بروتوكول للتأكيد ليس ضمانا لن يتلقى جهاز Apple الإخطار. أبل أيضا أيضا قائمة الانتظار في قائمة الانتظار الأخيرة. تبحث قليلا في الخدمة التي كنت قادرا على تحديد هذه الخدمة أكثر للحصول على الراحة للعميل، ولكن لا يمكن استخدامها لضمان الخدمة ويجب الجمع بين الأساليب الأخرى. قرأت هذا من المصدر التالي.

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

يبدو أن الإجابة ليست على ما إذا كنت تستطيع أن تخبر بالتأكيد أم لا. قد تتمكن من استخدام حزمة Sniffer مثل Wireshark لمعرفة ما إذا تم إرسالها، ولكن هذا لا يزال لا يضمن أنه تم استلامه وإرساله إلى الجهاز بسبب طبيعة الخدمة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top