سؤال

لديّ الحد الأدنى من مزود JMS ، والذي يرسل رسائل موضوع عبر رسائل UDP و SEEPE عبر TCP. أستخدم محددًا واحدًا للتعامل مع مفاتيح اختيار UDP و TCP (تسجيل كل من Socketchannels و DatagramChannels).

مشكلتي هي: إذا قمت فقط بإرسال وتلقي حزم UDP ، فكل شيء يسير على ما يرام ، ولكن بمجرد أن أبدأ الكتابة على مقبس TCP (باستخدام concector.wakeup () لجعل المحدد يقوم بالكتابة الفعلية) ، يدخل المحدد إلى Infinite حلقة ، وإعادة مجموعة مفاتيح اختيار فارغة ، وتناول وحدة المعالجة المركزية 100 ٪.

رمز الحلقة الرئيسية (مبسطة إلى حد ما) هو:

public void run() {
  while (!isInterrupted()) {
   try {
    selector.select();
   } catch (final IOException ex) {
    break;
   }

  final Iterator<SelectionKey> selKeys = selector.selectedKeys().iterator();
  while (selKeys.hasNext()) {
    final SelectionKey key = selKeys.next();
    selKeys.remove();
    if (key.isValid()) {
     if (key.isReadable()) {
      this.read(key);
     }
     if (key.isConnectable()) {
      this.connect(key);
     }
     if (key.isAcceptable()) {
      this.accept(key);
     }
     if (key.isWritable()) {
      this.write(key);
      key.cancel();
     }
    }
   }
   synchronized(waitingToWrite) {
    for (final SelectableChannel channel: waitingToWrite) {
     try {
      channel.register(selector, SelectionKey.OP_WRITE);
     } catch (ClosedChannelException ex) {
      // TODO: reopen
     }
    }
    waitingToWrite.clear();
   }
  }
 }

وبالنسبة لإرسال UDP (إرسال TCP متشابه):

public void udpSend(final String xmlString) throws IOException {
  synchronized(outbox) {
    outbox.add(xmlString);
  }
  synchronized(waitingToWrite) {
    waitingToWrite.add(dataOutChannel);
  }
  selector.wakeup();
}

إذن ، ما هو الخطأ هنا؟ هل يجب أن أستخدم محددين مختلفين للتعامل مع حزم UDP و TCP؟

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

المحلول 2

ذهبت المشكلة بعد الترقية إلى Java 1.6.0_22.

نصائح أخرى

أقترح عليك التحقق من قيمة إرجاع طريقة SELECT ().

try {
 if(selector.select() == 0) continue;
} catch (final IOException ex) {
 break;
}

هل حاولت تصحيح الأخطاء لمعرفة مكان الحلقة؟

يحرر:

  • أوصي بأنه بدلاً من استدعاء "إزالة ()" على التكرار ، يمكنك الاتصال بـ SelecteKeys.clear () بعد التكرار عليها. من الممكن أن يقوم تنفيذ التكرار ، لا يزيله من المجموعة الأساسية.
  • تأكد من أنك لا تسجل OP_CONNECT على قناة متصلة.

ربما تحصل على ioexception وتجاهلها في كتلة الصيد الفارغة. أبداً إفعل ذلك. والاستمرار فقط بعد IOException هو عمليًا أبدًا الإجراء الصحيح. الاستثناء الوحيد لتلك القاعدة التي يمكنني التفكير بها في Offhand هو SockettimeOutException ، وأنت في وضع عدم الحظر حتى لا تحصل على هذه ، ولا تحصل عليها على المحددات على أي حال. أرغب في رؤية محتوى الأساليب التي تتعامل مع الاتصال ، وقبولها ، وقراءتها ، والكتابة.

تعديل التصميم الخاص بك للحصول على مؤشر ترابط واحد لكل اتصال شبكة واردة.

يجب استخدام المحدد عند استخدام مؤشر ترابط واحد لمعالجة الرسائل الواردة على مآخذ TCP متعددة. تقوم بتسجيل كل مقبس مع المحدد ثم select(), ، والذي يحظر حتى يكون هناك بيانات متوفرة على أحدها. ثم يمكنك حلقة كل مفتاح ومعالجة بيانات الانتظار. هذه هي الطريقة التي استخدمتها دائمًا عند كتابة رمز C ، وسوف تعمل ، لكنني لا أعتقد أنها أفضل طريقة للقيام بذلك في Java.

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

إذا كنت ترغب في تشغيل هذا مع موضوع واحد فقط ، فيجب عليك استخدام المحدد فقط لمآخذ TCP حيث تريد اتصالات واردة. بهذه الطريقة ، المرة الوحيدة التي ندعو فيها select() سيعود هو عندما تكون هناك بيانات واردة تنتظر أحد المقابس. سيكون هذا الموضوع نائماً في كل أوقات أخرى ، ولن توقظه أي عملية أخرى.

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