جافا NIO تحديد العوائد () من دون مفاتيح المختارة - لماذا؟
سؤال
في كتابة بعض رمز اختبار قد وجدت لي أن Selector.select () يمكن العودة دون Selector.selectedKeys () تحتوي على أي من مفاتيح لهذه العملية. يحدث هذا في حلقة ضيقة عندما أسجل قناة إد استعرض () مع
SelectionKey.OP_READ | SelectionKey.OP_CONNECT
وكما عمليات الفائدة.
ووفقا للمستندات، حدد () يجب أن تعود في الحالات التالية:
1) وهناك القنوات التي يمكن اتخاذ إجراءات بشأنها.
2) أنت تدعو صراحة Selector.wakeup () - يتم اختيار أي مفاتيح
.و3) أنت صراحة Thread.interrupt () في موضوع القيام تحديد () - يتم اختيار أي مفاتيح
إذا أحصل على أي مفاتيح بعد تحديد () يجب أن تكون في حالة (2) و (3). ومع ذلك، قانون بلدي لا يدعو التنبيه () أو يقطع () لبدء هذه العوائد.
وأي أفكار حول ما يسبب هذا السلوك؟
المحلول
والجواب باختصار: إزالة OP_CONNECT
من قائمة العمليات كنت مهتما في لاتصال المقبولة - اتصال مقبول متصلا بالفعل
وتمكنت من إعادة إنشاء المشكلة، والتي قد تكون بالضبط ما يحدث لك:
import java.net.*;
import java.nio.channels.*;
public class MyNioServer {
public static void main(String[] params) throws Exception {
final ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(true);
serverChannel.socket().bind(new InetSocketAddress("localhost", 12345));
System.out.println("Listening for incoming connections");
final SocketChannel clientChannel = serverChannel.accept();
System.out.println("Accepted connection: " + clientChannel);
final Selector selector = Selector.open();
clientChannel.configureBlocking(false);
final SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_CONNECT);
System.out.println("Selecting...");
System.out.println(selector.select());
System.out.println(selector.selectedKeys().size());
System.out.println(clientKey.readyOps());
}
}
وبعد أن يتلقى الخادم أعلاه اتصال، وselect()
أولا جدا على اتصال يخرج دون عرقلة وعدم وجود مفاتيح مع عمليات جاهزة. أنا لا أعرف لماذا يتصرف جافا بهذه الطريقة، ولكن يبدو كثير من الناس الحصول على لعض من هذا السلوك.
والنتيجة هي نفسها على الشمس JVM 1.5.0_06 على نظام التشغيل Windows XP وكذلك JVM الشمس 1.5.0_05 و1.4.2_04 على لينكس 2.6.
نصائح أخرى
والسبب هو أن OP_CONNECT
وOP_WRITE
هي نفس الشيء تحت غطاء محرك السيارة، لذلك يجب أن لا تكون مسجلة على حد سواء في وقت واحد (كما سبق OP_ACCEPT
وOP_READ
)، ويجب أن لا تكون مسجلة لOP_CONNECT
على الإطلاق عند توصيل القناة بالفعل ، كما هو الحال في هذه القضية، بعد أن تم قبول.
وOP_WRITE
هو على استعداد دائما تقريبا، إلا عندما المخزن المؤقت إرسال مأخذ في البريد نواة مليء، لذا يجب عليك تسجيل فقط لأنه بعد الحصول على الكتابة طول صفري. لذلك من خلال تسجيل قناة متصلة بالفعل لOP_CONNECT,
كنت مسجلا حقا لOP_WRITE,
الذي كان على استعداد، لذلك select()
حصلت على تشغيلها.