جافا نيو:يؤدي إرسال رسائل كبيرة الحجم بسرعة إلى قطع الحزم وفقدان البيانات

StackOverflow https://stackoverflow.com/questions/499070

سؤال

لدي هذه المشكلة السيئة حيث يؤدي إرسال رسائل متعددة وكبيرة في تتابع سريع من خادم Java (NIO) (يعمل بنظام Linux) إلى العميل إلى حزم مبتورة.يجب أن تكون الرسائل كبيرة الحجم ويتم إرسالها بسرعة كبيرة حتى تحدث المشكلة.إليك بشكل أساسي ما يفعله الكود الخاص بي (ليس الكود الفعلي، ولكن ما يحدث بشكل أو بآخر):

//-- setup stuff: --
Charset charset = Charset.forName("UTF-8");
CharsetEncoder encoder = charset.newEncoder();
String msg = "A very long message (let's say 20KB)...";

//-- inside loop to handle incoming connections: --
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true);
sc.socket().setSendBufferSize(1024*1024);

//-- later, actual sending of messages: --
for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
  sc.write(bb);
  bb.rewind();
}

لذلك، إذا كانت الحزم طويلة بما فيه الكفاية وتم إرسالها في أسرع وقت ممكن (أي.في حلقة كهذه دون أي تأخير)، ثم على الطرف الآخر غالبًا ما يظهر شيء مثل هذا:

[COMPLETE PACKET 1]
[COMPLETE PACKET 2]
[COMPLETE PACKET 3]
[START OF PACKET 4][SOME OR ALL OF PACKET 5]

هناك فقدان للبيانات، وتبدأ الحزم في العمل معًا، بحيث تصل بداية الحزمة 5 (في هذا المثال) في نفس الرسالة مثل بداية الحزمة 4.لا يقتصر الأمر على الاقتطاع فحسب، بل يعمل على تشغيل الرسائل معًا.

أتخيل أن هذا مرتبط بمخزن TCP المؤقت أو "حجم النافذة"، أو أن الخادم هنا يوفر البيانات بشكل أسرع من نظام التشغيل، أو محول الشبكة، أو شيء من هذا القبيل، يمكنه التعامل معها.ولكن كيف يمكنني التحقق من ذلك ومنع حدوثه؟إذا قمت بتقليل طول الرسالة لكل استخدام لـ sc.write()، ولكن بعد ذلك قمت بزيادة التكرارات، فسوف أواجه نفس المشكلة.يبدو أنها مجرد مشكلة تتعلق بكمية البيانات في فترة زمنية قصيرة.لا أرى أن sc.write() يطرح أي استثناءات أيضًا (أعلم أنه في المثال أعلاه لم أقم بالتحقق، ولكني قمت بذلك في اختباراتي).

سأكون سعيدًا إذا كان بإمكاني التحقق برمجيًا مما إذا كان غير جاهز لمزيد من البيانات بعد، وتأخيره، والانتظار حتى يصبح جاهزًا.لست متأكدًا أيضًا مما إذا كان "sc.socket (). setSendBuffersize (1024*1024) ؛" له أي تأثير ، أو إذا كنت بحاجة إلى ضبط هذا على جانب Linux من الأشياء.هل هناك طريقة "لطرد" قناة المقبس حقًا؟كحل بديل ضعيف، يمكنني محاولة فرض إرسال كامل لأي شيء تم تخزينه مؤقتًا بشكل صريح في أي وقت أحاول فيه إرسال رسالة يزيد حجمها عن 10 كيلو بايت، على سبيل المثال (وهو ما لا يحدث كثيرًا في طلبي).لكنني لا أعرف أي طريقة لفرض إرسال المخزن المؤقت (أو الانتظار حتى يتم إرساله).شكرا على اي مساعدة!

هل كانت مفيدة؟

المحلول

وهناك العديد من الأسباب لماذا sc.write () لا ترسل بعض أو كافة البيانات. لديك للتحقق من قيمة الإرجاع و / أو عدد البايتات المتبقية في المخزن المؤقت.

for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
  if(sc.write(bb) > 0 && bb.remaining() == 0) {
    // all data sent
  } else {
    // could not send all data.
  }
  bb.rewind();
}

نصائح أخرى

وأنت لم التحقق من قيمة الإرجاع:

sc.write(bb);

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

وأنا لم تفعل أي برمجة NIO، ولكن وفقا لJavadocs، سوف sc.write () لا يكتب ByteBuffer بأكمله إذا كان SocketChannel في غير مؤمن وضع (كما هو لك) والمخزن المؤقت الإخراج مأخذ ممتلئ .

ولأن كنت تكتب بسرعة، فمن المرجح جدا أن تملأ الاتصال والشبكة أو جهاز الاستقبال الخاص بك لا يمكن مجاراتها.

<اقتباس فقرة>   <اقتباس فقرة>     

وسأكون سعيدا إذا كان بإمكاني تحقق برمجيا إذا لم يكن على استعداد لمزيد من بيانات حتى الآن

  

وتحتاج إلى التحقق من قيمة الإرجاع sc.write () لمعرفة ما إذا كان المخزن المؤقت الإخراج ممتلئ.

ولا تفترض لديك أي سيطرة على ما هي البيانات التي ينتهي فيها الحزمة.

والمزيد من هنا: <لأ href = "https://stackoverflow.com/questions/453609/whats-the-best-way-to-monitor-a-socket-for-new-data-and-then-process المفتى البيانات / 453951 # 453951 "> ما هي أفضل طريقة لمراقبة مأخذ لبيانات جديدة ثم عملية البيانات؟

أنت تستخدم وضع عدم الحظر:sc.configureBlocking(خطأ شنيع);

اضبط الحظر على حقيقي ويجب أن يعمل الكود الخاص بك كما هو.الاقتراحات المقدمة من الآخرين هنا للتحقق من عدد الإرسال والتكرار ستعمل أيضًا.

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