2012-06-07 2 views
3

Я построил систему на основе Selector на Java, которая может принимать несколько клиентов. Он имеет ServerSocketChannel, зарегистрированный в OP_ACCEPT, который принимает() s входящее соединение и снова регистрирует полученный SocketChannel с селектором. Вот что бит:Java SocketChannel register() для нескольких OP-кодов никогда не выбрано

ServerSocketChannel insock = ServerSocketChannel.open(); 
    insock.configureBlocking(false); 
    insock.socket().bind(new InetSocketAddress(6789)); 

    Selector sel = Selector.open(); 
    SelectionKey joinchannel = insock.register(sel, SelectionKey.OP_ACCEPT); 

    System.out.println("Ready to accept incoming connections."); 

    while (true) { 
     int ready = sel.selectNow(); 
     if (ready == 0) 
      continue; 
     Set<SelectionKey> selectedKeys = sel.selectedKeys(); 
     Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); 
     while (keyIterator.hasNext()) { 
      SelectionKey key = keyIterator.next(); 
      if(key.isAcceptable()){ 
       SocketChannel newConnection = insock.accept(); 
       System.out.println("New client "+newConnection+" connected."); 
       newConnection.configureBlocking(false); 
       newConnection.register(sel, SelectionKey.OP_READ).attach(new DGPlayer()); 
      } 

Если зарегистрировать новые SocketChannel для OP_READ, это работает отлично. Проверка на isReadable() завершается успешно, считывает данные. Вот что бит:

else if(key.isReadable()){ 
       ByteBuffer buf = ByteBuffer.allocate(1024); 
       int trans = ((SocketChannel)key.channel()).read(buf); buf.flip(); 
       byte[] ba = new byte[buf.remaining()]; buf.get(ba); 
       String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8")); 

       if(trans > 0){ 
        DGPlayer client = (DGPlayer) key.attachment(); 
        System.out.println(client.name+": "+msg.trim()); 
       } 
      } 
       //    else if(key.isWritable()){ 
//     ByteBuffer buf = ByteBuffer.allocate(48); 
//     buf.clear(); 
//     buf.put(((String)key.attachment()).getBytes()); 
//     buf.flip(); 
//     
//     SocketChannel target = ((SocketChannel)key.channel()); 
//     while(buf.hasRemaining()) { 
//      target.write(buf); 
//     } 
//     
//     key.attach(null); 
//    } 

Если я зарегистрировать SocketChannel для OP_READ | OP_WRITE, однако, ничего не происходит. Насколько я могу судить, селектор никогда не находит канал как читаемым(), так и записываемым(). Единственное изменение - это регистрация канала для разрешения WRITE.

Любая идея, почему это может быть?

EDIT - Ради ЗАВЕРШЕНИЕ, вот некоторый код клиента:

while (true) { 
     int readyChannels = sel.select(); 
     if (readyChannels == 0) 
      continue; 

     Set<SelectionKey> selectedKeys = sel.selectedKeys(); 
     Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); 

     while (keyIterator.hasNext()) { 
      SelectionKey key = keyIterator.next(); 

      if (key.isReadable()) { 
       ByteBuffer buf = ByteBuffer.allocate(1024); 
       int trans = ((SocketChannel)key.channel()).read(buf); buf.flip(); 
       byte[] ba = new byte[buf.remaining()]; buf.get(ba); 
       String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8")); 

       if(msg.trim().length() != 0) 
        System.out.println("Response from server: "+msg.trim()); 
      } 
      else if(key.isWritable() && messages.size() > 0){ 
       String message = messages.remove(); 
       ByteBuffer buf = ByteBuffer.allocate(48); 
       buf.clear(); 
       buf.put(message.getBytes(Charset.forName("UTF-8"))); 

       buf.flip(); 

       int written = outsock.write(buf); 
       while(buf.hasRemaining()){ 
        outsock.write(buf); 
       } 

       System.out.println("Wrote '"+message+"' to server"); 
      } 
      keyIterator.remove(); 
     } 
    } 

(Клиент регистрирует сервер для OP_READ | OP_WRITE)

+0

Где мой код? – Jeffrey

+0

А, ладно. Я надеялся, что это будет что-то очевидное! Я отправлю код за секунду. – mtrc

+1

Даже если это что-то очевидное, никогда не помешает опубликовать свой код, когда вы задаете вопрос. – Jeffrey

ответ

7

В целом это плохая идея, что канал зарегистрирован OP_WRITE если вам нечего активно писать. Большинство каналов могут быть записаны в большинстве случаев, поэтому каждый вызов Selector.select будет возвращаться без блокировки и потребления ресурсов. Лучше добавить флаг записи в SelectionKey.interestOps, когда вы будете готовы что-то записать на канал и удалите флаг, когда закончите.

+1

Большое спасибо, теперь я меняю свой интерес, как и когда нужно. Я думаю, что я, наконец, понял Селекторы сейчас ... – mtrc

+0

@mtrc. Даже для того, чтобы просто писать *, когда вы что-то пишете, используйте только OP_WRITE, когда вы сталкиваетесь с короткой записью, то есть зарегистрируйтесь для OP_WRITE, чтобы повторить операцию записи , и отмените регистрацию, как только попытка повторится. Это немного сложнее для программирования, но это экономит время ожидания. – EJP

Смежные вопросы