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. 0이 반환되면 buffer.remaining()이 0이면 완료됩니다. compact() 버퍼가 반환됩니다.
  3. 제로와 buffer.remaining() -zero입니다 반환하는 경우는, 소켓 전송 버퍼는 당신이 그래서 (A) (B) 대신 OP_READ 및 선택에 (c) 반환의 OP_WRITE에 등록 버퍼를 압축, 가득 고리. 채널이 쓰기 가능 해지면 위의 (1)에서와 같이 반복하십시오. 이번에는 (2)와 같이 성공하면 OP_WRITE 대신 OP_READ에 등록하십시오. 즉, 소켓 전송 버퍼에 공간이있을 때 셀렉터가 알려주기를 기다리고 있습니다. 그것을 사용하려고 노력한다. 그리고 글쓰기를 끝내면 다시 끝납니다.