2013-06-21 5 views
0

클라이언트간에 메시지를 교환하는 서버를 작성하고 있습니다. 해결해야 할 한 가지 문제는 클라이언트가 닫힐 때 채널을 해제하는 방법입니다. 내가하는 일은 all-Clients 맵이 모니터되는 모니터 스레드를 시작하고 write()를 시도 할 때 예외가 감지되면 채널을 제거()하려고 시도하는 것입니다. 그러나 클라이언트를 닫은 후에는 모니터 스레드의 write() 메서드가 예외를 throw하지 않으므로 쓸모없는 채널이 해제되지 않습니다. 왜 그런지 알아? TCP 소켓에 쓰기SocketChannel의 write() 메서드는해야 할 때 예외를 throw하지 않습니다.

public class ServerMonitor extends Thread{ 
private Map<String, SocketChannel> allClients; 
private Set set; 
private Iterator it; 
private Entry entry; 
private SocketChannel channel; 
private ByteBuffer buf; 

public ServerMonitor(Map<String, SocketChannel> allClients) { 
    this.allClients = allClients; 
    buf = ByteBuffer.allocateDirect(10); 
    byte b = 0; 
    buf.put(b); 
    buf.flip(); 
} 

public void run(){ 
    while(true) { 
     if(!allClients.isEmpty()) { 
      set = allClients.entrySet(); 
      it = set.iterator(); 
      while(it.hasNext()) { 
       entry = (Entry) it.next(); 
       channel = (SocketChannel) entry.getValue(); 
       try{ 
        channel.write(buf); 
       } catch(Exception e) { 
        allClients.remove(entry.getKey()); 
        //set.remove(entry); 
       } 
      } 
     } 
     try { 
      Thread.sleep(1000 * 5); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

}

+0

'this.allClients = allClients; '<- 당신은 정말 당신이 무엇을하고 있는지 알지 못한다면 이것은 매우 위험하다 ... 호출자가지도를 수정하는 경우, 클래스'지도 변경 사항을 볼 것이다! 'ConcurrentModificationException'을위한 좋은 소스 ... – fge

+0

@fge 이것을 수행하고 싶습니다 .allClients = allClients; 왜냐하면 모니터 스레드가 바이트를 쓸 수없는 채널을 발견했을 때 소스 allClients (쓸모없는 채널 해제)를 수정해야하기 때문입니다. – dastan

답변

1

로컬 버퍼 및 비동기 전선에 배치됩니다. 따라서 피어가 닫힌 후 첫 번째 쓰기 작업에 의존 할 수 없습니다. 후속 쓰기 실패에 의존 할 수 있지만 거기에 도달하려면 많은 쓰기가 필요할 수 있습니다.

+0

그래서 첫 번째 쓰기에서 예외를 감지 할 수 있도록 bytebuffer.size() 바이트를 채널에 쓸 수 있습니까? – dastan

+0

방금 ​​대답했습니다. ByteBuffer.size()는 아무 관계가 없습니다. – EJP

+0

크기는 TCP 자체에 따라 좌우됩니까? – dastan

0

TCP를 통해 데이터를 보내는 응용 프로그램을 작성할 때이 문제가 발생했습니다. 클라이언트가 연결을 닫았는지 알 수있는 유일한 방법은 write(...)으로 전화하여 IOException입니다. 이것은 거의 작동하는 방식입니다.

클리너 솔루션이 있습니다. 무엇보다 먼저 클라이언트가 알지 못하는 사이에 연결을 끊는 경우를 처리하고 IOExceptionwrite(...)에 올리면 올바르게 제거해야합니다. 그러나 클라이언트가 서버에 연결 해제 중임을 알리는 메시지를 보내면이를 사용하여 연결을 종료 할 수 있습니다.

+0

네가 말한 것처럼 실제로 네가 말하고 있지만 (...) IOException을 발생시키지 않는 호출은 혼란 스럽다. – dastan

+0

EJP가 말했듯이, 특정 쓰기 실패에 의존 할 수는 없지만 결국 실패 할 것입니다. 소켓에 쓸 데이터가 많을수록 빠를수록 빠릅니다. – VolatileDream