2014-09-16 2 views
0

Я хочу написать клиентское приложение, которое отправляет сообщения на сервер и получает его ответ. Клиент отправляет сообщения много раз (например, одно сообщение в очень секунду периодически) независимо от ответов. Когда ответ возвращается, клиент хочет ответить как можно быстрее.java.nio.channels.SocketChannel для периодической записи и немедленного чтения

Вот код, который не работает, клиента. Я хочу, чтобы исполняемый экземпляр в методе startReading() отвечал на ответ с сервера, но он этого не делает. В этом случае _channel.write(buffer) не возвращается должным образом.

Пожалуйста, дайте мне знать о проблеме следующего кода или какого-либо другого способа реализации описанного выше поведения.

public class MyClient { 

    private SocketChannel _channel = null; 
    private Selector _selector = null; 
    private InetSocketAddress _addr = new InetSocketAddress("127.0.0.1", 5555); 

    public MyClient() { 
     _selector = SelectorProvider.provider().openSelector(); 
     _channel = SocketChannel.open(); 
     _channel.configureBlocking(false); 
     startReading(); 
     _channel.connect(_addr); 
    } 

    private void startReading() throws IOException { 
     ByteBuffer buffer = ByteBuffer.allocate(1024); 
     _channel.register(_selector, SelectionKey.OP_READ, buffer); 
     Runnable runnable = new Runnable() { 
      @Override 
      public void run() { 
       try { 
        while (0 < _selector.select()) { 
         Iterator<SelectionKey> keyIterator = _selector.selectedKeys().iterator(); 
         while (keyIterator.hasNext()) { 
          SelectionKey key = keyIterator.next(); 
          keyIterator.remove(); 
          if (key.isReadable()) 
           read(key); 
         } 
        } 
       } 
       catch (IOException e) {} 
      } 
     }; 
     ExecutorService service = Executors.newFixedThreadPool(1); 
     service.execute(runnable); 
    } 

    private void read(SelectionKey key) throws IOException { 
     // do some reading operations 
    } 

    @Override 
    public void run() { 
     ByteBuffer buffer = ByteBuffer.allocate(1024); 
     // write message to buffer 
     buffer.flip(); 
     try { 
      _channel.write(buffer); 
     } catch (IOException e) {} 
    } 

    public static void main (String[] args) { 
     MyClient client = new MyClient(); 
     ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor(); 
     ex.scheduleAtFixedRate(client, 1000, 1000, TimeUnit.MILLISECONDS); 
    } 
} 
+1

Почему вы возитесь с селектором для одного канала? В конце концов, то, что вы эффективно делаете в своем фоновом потоке, - это повторное внедрение блокирующего чтения. Таким образом, вы можете просто настроить канал для блокировки и сделать обычное чтение в фоновом потоке без этой сложной структуры. – Holger

+0

Зачем использовать селектор для одиночного канала? Поскольку приведенный выше код является упрощенной моделью моей проблемы. То, что я действительно хочу сделать, сложнее. – user4047360

ответ

1

Вы игнорируете код возврата от channel.write(). Он не обязан писать весь буфер. В неблокирующем режиме он вообще не обязан ничего писать.

Вы должны сделать следующее:

  1. Хотя это возвращает положительное значение, то есть он написал что-то, петля.
  2. Если он возвращает нуль иbuffer.remaining() - 0, вы закончили: compact() буфер и возврат.
  3. Если она возвращает ноль и buffer.remaining() является не -нуля, буфер сокета полон, так что вы должны (а) уплотнить буфер (б) зарегистрировать OP_WRITE вместо OP_READ и (с) вернуться к выбери петля. Когда канал станет доступен для записи, повторите с (1) выше, и на этот раз, если вы добьетесь успеха, как на (2), вернитесь к регистрации на OP_READ вместо OP_WRITE. Другими словами, вы ожидаете, когда селектор скажет вам, когда в буфере отправки сокетов есть место; пытаясь использовать его; и если вам удастся завершить запись, вы снова это сделаете.
Смежные вопросы