لماذا SocketChannel يكتب إكمال دائما لكامل المبلغ حتى على مآخذ غير مؤمن؟

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

سؤال

واستخدام صن جافا VM 1.5 أو 1.6 على ويندوز، أقوم بتوصيل مأخذ غير مؤمن. وبعد ذلك ملء ByteBuffer برسالة إلى الإخراج، ومحاولة write() إلى SocketChannel.

وأتوقع أن الكتابة لاستكمال جزئيا فقط إذا كان المبلغ المراد كتابتها أكبر من مقدار المساحة في المخزن المؤقت الإخراج TCP المقبس (هذا هو ما أتوقع بشكل حدسي، كما انها الى حد كبير فهمي في مستندات )، ولكن هذا ليس ما يحدث. وwrite() <م> دائما يبدو أن العودة الإبلاغ عن كامل المبلغ مكتوبة، حتى لو كان عدة ميغابايت (SO_SNDBUF مأخذ هو 8KB، بكثير، وأقل بكثير من رسالتي الناتج متعددة ميغا بايت).

وثمة مشكلة هنا هي أنني لا يمكن اختبار التعليمات البرمجية التي تعالج الحالة حيث يتم كتابة جزء من الانتاج (تسجيل مجموعة مصلحة WRITE إلى محدد والقيام select() الانتظار حتى يمكن كتابة ما تبقى)، كما هذه الحالة لا يبدو أن يحدث. ما أنا لا تفهم؟

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

المحلول

وتمكنت من إعادة إنتاج الحالة التي قد تكون مماثلة ليدكم. أعتقد، ومن المفارقات بما فيه الكفاية، المتلقي تستهلك البيانات بشكل أسرع مما كنت كتابة هذا التقرير.

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");

    final InputStream in = cs.getInputStream();
    final byte[] tmp = new byte[64 * 1024];
    while (in.read(tmp) != -1);

    Thread.sleep(100000);
  }
}



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class MyNioClient {
  public static void main(String[] args) throws Exception {
    final SocketChannel s = SocketChannel.open();
    s.configureBlocking(false);
    s.connect(new InetSocketAddress("localhost", 12345));
    s.finishConnect();

    final ByteBuffer buf = ByteBuffer.allocate(128 * 1024);
    for (int i = 0; i < 10; i++) {
      System.out.println("to write: " + buf.remaining() + ", written: " + s.write(buf));
      buf.position(0);
    }
    Thread.sleep(100000);
  }
}

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

والناتج عندما يكون الملقم يقرأ من الاتصال:

to write: 131072, written:  131072
to write: 131072, written:  131072
to write: 131072, written:  131072
...

والناتج عند الملقم لا يقرأ من الاتصال:

to write: 131072, written:  131072
to write: 131072, written:  0
to write: 131072, written:  0
...  

نصائح أخرى

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

وأود أيضا أن الرهان ليس غاية التي قد تجد تنفيذ أفضل من ما كنت أبحث عنه في أي تنفيذ JVM الأخرى، مثل آي بي إم، ولكن لا يمكنني ان اضمن انه بدون ننظر إليها نفسي.

وأنا لا يمكن العثور عليه في أي مكان موثقة، ولكن IIRC [1]، وإرسال () مكفول إما أ) إرسال المخزن المؤقت زودت تماما، أو ب) تفشل. أنها ستكمل أبدا إرسال جزئيا.

[1] لقد كتبت عدة تطبيقات WINSOCK (لوين 3.0، فوز 95، وين NT، الخ)، وحتى هذا قد يكون WINSOCK محددة (بدلا من مآخذ العامة) السلوك.

وأنا سوف تجعل قفزة كبيرة من الإيمان ونفترض أن مزود الشبكة الكامن وراء جافا هو نفسه لC ... وO / S يخصص أكثر من SO_SNDBUF فقط عن كل مأخذ. أراهن انك اذا وضعت كود الإرسال الخاص بك في ل(1،100000) حلقة، وكنت في نهاية المطاف الحصول على الكتابة أن ينجح مع قيمة أصغر من المطلوب.

هل حقا ينبغي أن ننظر في إطار بظاهرة مثل MINA أو <وأ href = "HTTPS: // أشيب .dev.java.net / "يختلط =" نوفولو noreferrer "> الدب . لقد استعملت MINA مع نجاحا كبيرا في ملقم المؤسسة الدردشة. كما أنها تستخدم في rel="nofollow Openfire الدردشة الخادم. يستخدم الدب في تنفيذ الشمس JavaEE.

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

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