[英]Java SocketChannel Eating my bytes
我創建了一個到遠程服務器的SocketChannel,以在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返回並指示有可從套接字讀取的數據時,應僅在重新進入對select()的調用之前讀取可用的字節。 如果read()返回-1,則表明緩沖區中沒有更多字節可立即讀取-這並不意味着套接字已關閉。 因此,我懷疑您嘗試在返回之前完全填充緩沖區是不正確的。 即使工作正常,在數據到達時,您的I / O線程也會不斷旋轉。 特別是,看起來您只是在忽略返回值-1。
考慮重新構造代碼以使用有限狀態機方法。 例如,我過去使用3狀態模型來實現此功能:IDLE,READ_MESSAGE_LENGTH和READ_MESSAGE。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.