내 바이트를 먹는 자바 양말 채널
-
20-09-2019 - |
문제
Tomcat에서 메시지를 보내고 받기 위해 원격 서버에 양말을 만들었습니다. 원격 컴퓨터에서 메시지를 받기 위해 작업 전용 스레드를 사용했습니다 (이 스레드 만 소켓에서 읽지 않습니다).
일부 바이트가 Socketchannel에서 수신되면 (새 데이터의 비 차단 모드에서 Socketchannel을 계속 폴링하는 경우), 다음 메시지의 길이를 얻기 위해 4 바이트를 읽은 다음, Socketchannel에서 x 바이트를 할당하고 읽습니다. 그런 다음 디코딩하고 메시지로 재구성했습니다.
아래는 수신 스레드에 대한 내 코드입니다.
@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에서받은 첫 번째 바이트는 괜찮으며 메시지를 성공적으로 해독했습니다. 그러나 다음에 Socketchannel에서 읽을 때 소켓은 약 100 바이트를 건너 뛰었으므로 잘못된 바이트를 읽고 길이로 해석하여 모든 것이 손상되었습니다.
코드에 무슨 문제가 있습니까? Socketchannel에서 다른 스레드가 읽지 않습니다.
해결책
괄호가 꺼져 있고 코드는 다음과 같습니다.
(0xFF & ((int)header[1] << 8))
항상 0 (<< 16 및 << 24와 동일)입니다. 제 생각에는 다음과 같습니다.
((0xFF & ((int)header[1])) << 8)
이것은 충분한 메시지 바이트를 읽지 않고 동기화에 불일치로 이어질 것입니다 (너무 많은 것을 읽는 것과는 달리).
편집하다: 이제 위의 내용을 수정하면 잘못된 것을 볼 수 없습니다. 첫 번째 메시지의 길이와 먹는 바이트의 정확한 수 사이의 관계를 알려 주시겠습니까?
표시된 코드를 기반으로, 내 유일한 추측은 Schannel에 영향을 줄 수있는 샘플에서 일부 동작을 편집했다고 생각합니다. Schannel은 다른 곳에서 참조됩니까?
라인 인 경우 :
ByteBuffer buf = ByteBuffer.allocate(4);
외부에있을 것입니다 while
그것은 당신이 설명하는 행동을 초래하지만 샘플 코드에서는 그렇지 않습니다.
다른 팁
비 블로킹 모드에서 소켓을 폴링한다고 말할 때 "표준"을 사용하고 있음을 의미합니다. Selector.select()
접근하다?
SELECT가 반환되고 소켓에서 읽을 수있는 데이터가 있음을 나타내는 경우 선택 ()에 호출을 다시 입력하기 전에 사용할 수있는 바이트 만 읽어야합니다. read ()가 -1을 반환하면 버퍼에서 즉시 판독 할 수있는 더 이상 바이트를 사용할 수 없음을 나타냅니다. 소켓이 닫혔다는 의미는 아닙니다. 따라서 반환하기 전에 버퍼를 완전히 채우려는 시도가 잘못되었다고 생각합니다. 작동하더라도 데이터가 도착하는 동안 I/O 스레드가 지속적으로 회전됩니다. 특히, 단순히 -1의 반환 값을 무시하고있는 것 같습니다.
유한 상태 머신 접근법을 사용하려면 코드를 재건하는 것을 고려하십시오. 예를 들어, 과거에는 3 국가 모델을 사용하여이를 구현했습니다.