2009-11-16 1 views
1

Tomcat에서 메시지를 보내고 받기 위해 원격 서버에 SocketChannel을 만들었습니다. 원격 컴퓨터에서 메시지를 받으려면 작업 전용 스레드를 사용했습니다 (이 스레드는 소켓에서 읽는 것 외에는 아무것도 읽지 않습니다).Java SocketChannel 내 바이트를 먹는 중

일부 바이트가 SocketChannel에서 수신되면 (새 데이터에 대해 비 차단 모드로 SocketChannel을 폴링 함) 다음 메시지의 길이를 얻기 위해 4 바이트를 읽은 다음, 다음 바이트를 할당하고 읽습니다. SocketChannel은 디코딩되어 메시지로 재구성됩니다.

다음은 수신 스레드에 대한 내 코드입니다 : 나는에 SocketChannel로부터받은

@Override 
public void run() { 

    while (true) { //Don't exit thread 

     //Attempt to read the size of the incoming message 
     ByteBuffer buf = ByteBuffer.allocate(4); 

     int bytesread = 0; 
     try { 
      while (buf.remaining() > 0) { 
       bytesread = schannel.read(buf); 

       if (bytesread == -1) { //Socket was terminated 

       } 

       if (quitthread) break; 
      } 

     } catch (IOException ex) { 

     } 

     if (buf.remaining() == 0) { 
      //Read the header 
      byte[] header = buf.array(); 
      int msgsize = (0xFF & (int)header[0]) + ((0xFF & (int)header[1]) << 8) 
        + ((0xFF & (int)header[2]) << 16) + ((0xFF & (int)header[3]) << 24); 

      //Read the message coming from the pipeline 
      buf = ByteBuffer.allocate(msgsize); 
      try { 
       while (buf.remaining() > 0) { 
        bytesread = schannel.read(buf); 

        if (bytesread == -1) { //Socket was terminated 

        } 

        if (quitthread) break; 
       } 
      } catch (IOException ex) { 

      } 

      parent.recvMessage(buf.array()); 
     } 

     if (quitthread) { 
      break; 
     } 
    } 

} 

첫 번째 바이트는 괜찮습니다, 나는 메시지를 성공적으로 디코딩. 그러나 다음 번에 SocketChannel에서 읽을 때 소켓은 약 100 바이트를 건너 뜁니다. 이로 인해 잘못된 바이트가 읽히고 길이가 해석되어 모든 내용이 손상됩니다.

코드에 어떤 문제가 있습니까? 다른 스레드가 SocketChannel에서 읽는 중입니다.

+0

헤더 버퍼를 4 바이트의 arry로 변환하는 대신 getInt()를 호출하여 길이를 읽을 수 있습니다. 이것을 부호없는 int로 처리하려면 항상 0xFFFFFFFF의 long 및 bitwise AND로 변환 할 수 있습니다. 또한, – Adamski

+0

자바가 작은 엔디안으로 바이트 []의 arr [4]를 읽는지 확신 할 수 없지만 아마 할 수 있습니다. 어쨌든, 나는 이것이 문제의 원인이라고 의심합니다. 첫 번째 메시지 길이가 정확하며 그 바이트 수를 읽습니다. 읽은 메시지를 검사해도 오류가 없음을 알 수 있습니다. 헤더의 다음 4 바이트를 읽으려고 할 때만 문제가 있습니다. – futureelite7

답변

1

귀하의 괄호가 꺼져 코드는 다음과 같습니다

(0xFF & ((int)header[1] << 8)) 

항상 (< < 16 < < (24)와 같은) 0이다, 내 생각 엔 당신이 의미 :

((0xFF & ((int)header[1])) << 8) 

이 너무 많은 바이트를 읽지 못하게되어 동기화가 일치하지 않게됩니다 (너무 많이 읽는 것과 반대).

편집 : 위 내용을 수정 했으므로 잘못된 것이 없습니다. 첫 번째 메시지의 길이와 먹는 정확한 바이트 수 사이의 관계를 말할 수 있습니까?

표시된 코드에 따르면, 내 유일한 추측은 표시된 채널에서 영향을 줄 수있는 일부 샘플을 편집 한 것입니다. 다른 채널에서 언급 된 채널이 있습니까?

만약 라인 :

ByteBuffer buf = ByteBuffer.allocate(4); 

는 당신이 설명하는 동작이 발생할 것 while 이외의 것,하지만 샘플 코드가 없습니다.

+0

헤더를 디코딩하는 데 문제가있는 것처럼 보입니다. 지금은 잘 작동합니다. – futureelite7

0

비 차단 모드로 소켓을 폴링한다고 가정하면 "표준"Selector.select() 접근 방식을 사용하고 있다는 의미입니까?

select가 반환되고 소켓에서 읽을 수있는 데이터가 있음을 나타내면 select() 호출을 다시 입력하기 전에 사용 가능한 바이트 만 읽어야합니다. read()가 -1을 반환하면 버퍼에서 즉시 읽을 수있는 바이트가 더 이상 없음을 나타냅니다. 소켓이 닫혀 있다는 것을 의미하지는 않습니다. 따라서 반환하기 전에 버퍼를 완전히 채우려는 시도가 잘못된 것으로 의심됩니다. 작동하더라도 데이터가 도착하는 동안 I/O 스레드가 계속 회전합니다. 특히 -1의 반환 값을 무시하는 것처럼 보입니다.

유한 상태 시스템 접근법을 사용하도록 코드를 다시 설계하십시오. 예를 들어, 과거에 IDLE, READ_MESSAGE_LENGTH 및 READ_MESSAGE와 같은 3 가지 상태 모델을 사용하여이를 구현했습니다.

+0

스레드 당 소켓이 하나뿐이므로 Selector.Select()를 사용해야하는지 여부는 알 수 없습니다. 어쨌든 -1은 스트림의 끝에 도달했음을 의미합니다 = 부모에게 알려야합니다. 방금 처리를하지 않았습니다. 일반 소켓()을 사용하여 어떻게 동작하는지 살펴 보겠습니다. – futureelite7