2009-01-31 2 views
3

Java NIO (Linux를 실행하는) 서버에서 클라이언트로 여러 개의 큰 메시지를 빠르게 연속적으로 보내는 것이 좋지 않은 문제가 있습니다. 잘린 패킷. 메시지는 커야하며 문제가 발생하기 위해서는 매우 빠르게 보내야합니다. 여기에 내 코드가 무엇을하고 있는지 기본적입니다 (실제적인 코드를하지만, 더 또는 덜 무슨 일이 일어나고 있는지) : 그래서Java NIO : 대용량 메시지를 빨리 보내면 잘리는 패킷 및 데이터 손실이 발생합니다.

//-- setup stuff: -- 
Charset charset = Charset.forName("UTF-8"); 
CharsetEncoder encoder = charset.newEncoder(); 
String msg = "A very long message (let's say 20KB)..."; 

//-- inside loop to handle incoming connections: -- 
ServerSocketChannel ssc = (ServerSocketChannel)key.channel(); 
SocketChannel sc = ssc.accept(); 
sc.configureBlocking(false); 
sc.socket().setTcpNoDelay(true); 
sc.socket().setSendBufferSize(1024*1024); 

//-- later, actual sending of messages: -- 
for (int n=0; n<20; n++){ 
    ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0')); 
    sc.write(bb); 
    bb.rewind(); 
} 

, 패킷이 같은 루프에서 빨리 즉, (가능한 한 충분히 길고 보낸 경우 지연없이)으로, 다음 다른 쪽 끝에서 종종이 같은 온다 :

[COMPLETE PACKET 1] 
[COMPLETE PACKET 2] 
[COMPLETE PACKET 3] 
[START OF PACKET 4][SOME OR ALL OF PACKET 5] 

이 데이터 손실 및 패킷 함께 실행하기 시작 등이 예에서 패킷 (5)의 시작() 패킷 4의 시작과 동일한 메시지에 도착합니다. 메시지를 함께 실행하는 것만으로 끝나는 것이 아닙니다.

이것이 TCP 버퍼 또는 "창 크기"와 관련이 있거나 서버가 OS 또는 네트워크 어댑터보다 빠른 데이터를 제공하고 있다고 생각합니다. 그런데 어떻게 확인하고 그것을 방지합니까? sc.write() 사용 당 메시지 길이를 줄이지 만 반복 횟수를 늘리면 같은 문제가 발생합니다. 짧은 시간 내에 데이터 양에 문제가있는 것처럼 보입니다. 나는 sc.write()가 예외를 던지고있는 것을 보지 못합니다. (위의 예제에서 저는 검사하지 않지만 내 테스트에는 있습니다).

프로그래밍 방식으로 데이터가 아직 준비되지 않았는지 확인하고 지연을 넣고 준비 될 때까지 기다릴 수 있으면 행복 할 것입니다. 또한 "sc.socket() .setSendBufferSize (1024 * 1024);" 어떤 영향을 미치는지, 또는 Linux 측에서이를 조정해야 할 필요가 있다면. SocketChannel을 실제로 "플러시"할 수있는 방법이 있습니까? 절름발이 해결 방법으로, 내가 명시 적으로 10KB 이상의 메시지를 보내려고 할 때 버퍼링 된 모든 내용을 강제로 보내도록 시도 할 수 있습니다 (예 : 응용 프로그램에서 자주 사용하지 않음). 그러나 나는 버퍼 전송을 강제로 (또는 전송할 때까지 기다리는) 방법을 모른다. 어떤 도움을 주셔서 감사합니다!

답변

6

sc.write()가 일부 또는 모든 데이터를 보내지 않는 데는 여러 가지 이유가 있습니다. 반환 값 및/또는 버퍼에 남아있는 바이트 수를 확인해야합니다.

for (int n=0; n<20; n++){ 
    ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0')); 
    if(sc.write(bb) > 0 && bb.remaining() == 0) { 
    // all data sent 
    } else { 
    // could not send all data. 
    } 
    bb.rewind(); 
} 
3

NIO 프로그래밍을 수행하지는 않았지만 Javadocs에 따르면 SocketChannel이 비 블로킹 모드 (귀하의 것과 같음)이고 소켓의 출력 버퍼가 인 경우 Javadoc에 따라 sc.write()가 전체 ByteBuffer를 작성하지 않습니다. 완전한.

당신이 너무 빨리 작성했기 때문에 연결이 너무 많아서 네트워크 나 수신기가 계속 작동하지 않을 가능성이 높습니다.

아직 더 많은 데이터 당신은 sc.write의 반환 값을 확인해야

준비되지 않은 경우 내가 프로그래밍 확인할 수 있다면 행복 할 것() 찾을 수 아웃풋 버퍼가 가득 찼는 지 확인하십시오.

sc.write(bb); 

이것은 당신의 버퍼에서 사용할 수있는 데이터보다 작을 수 있습니다 기록 된 바이트의 수를 반환

4

당신은의 반환 값을 확인하지 않습니다. nio가 작동하는 방식 때문에 바이트 버퍼에서 remaining()을 호출하여 왼쪽이 있는지 확인할 수 있습니다.

+0

예! 네트워크를 통해 데이터를 빠르게 보낼 수 있다고 기대하는 것은 합리적이지 않습니다. 차단하지 않으면 쓰기 결과를 테스트해야합니다. – erickson

1

어떤 데이터로 끝나는 패킷을 제어 할 수 있다고 가정하지 마십시오. 여기

더 : What's the best way to monitor a socket for new data and then process that data?

+0

"sc.socket(). setTcpNoDelay (true);" 여러 메시지가 단일 패킷으로 결합되지 않도록하는 데 도움이됩니다. – DivideByHero

+0

나는 그 메소드 호출에 완전히 익숙하지 않지만 도움! = 보장. 위의 링크에서 설명한 기술은 더 신중해야 함을 의미합니다. –

1

당신은 비 블록 모드 사용 : sc.configureBlocking (거짓을);

으로 차단을 설정하면 코드가 그대로 작동해야합니다. 여기에 다른 사람이 보내는 카운트와 루프를 확인하기위한 제안도 효과가 있습니다.

관련 문제