2009-11-16 64 views
1

我創建了一個SocketChannel到遠程服務器在Tomcat上發送和接收消息。爲了接收來自遠程計算機的消息,我使用了專用於任務的線程(只有此線程纔會從套接字讀取,沒有其他)。Java的一個SocketChannel吃我的字節

當在SocketChannel接收到一些字節時(我繼續在非阻塞模式下爲新數據輪詢SocketChannel),我首先讀取4個字節來獲取下一個消息的長度,然後分配和讀取x字節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第一字節是好的,我成功地解碼該消息。然而,下次我從SocketChannel讀取時,套接字跳過了大約100個字節,導致錯誤的字節被讀取並解釋爲長度,導致一切損壞。

代碼有什麼問題?沒有其他線程正在讀取SocketChannel。

+0

您可以簡單地調用getInt()來讀取長度,而不是將頭緩衝區轉換爲4字節的長度。如果你想把它當作一個無符號整數,你總是可以用0xFFFFFFFF進行長整型AND運算。另外, – Adamski 2009-11-16 17:02:20

+0

我可能可以,但我不確定java是否會像byte endian那樣讀取byte []的arr [4]。 無論如何,我懷疑這是問題的原因。第一個消息長度是正確的,我讀了這個字節數。檢查讀取的消息也表明沒有錯誤。當我嘗試讀取頭的下4個字節時,只有一個問題。 – futureelite7 2009-11-16 17:06:37

回答

1

你的括號關閉,代碼:

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

這始終是0(同與< < 16和< < 24),我的猜測是你的意思是:

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

這會導致閱讀不夠消息字節,也同步導致錯配(而不是閱讀太多了。)

編輯:現在你修正了上面的問題,我看不到任何錯誤。你能告訴我們第一條消息的長度和被吃掉的確切字節數之間的關係嗎?

根據顯示的代碼,我唯一的猜測是,你編輯的一些行爲進行顯示的樣本可能影響SCHANNEL,是SCHANNEL其他地方引用?

如果線路:

ByteBuffer buf = ByteBuffer.allocate(4); 

將是while這將導致您所描述的行爲之外,但在你的示例代碼並非如此。

+0

看起來這是解碼頭部時遇到的問題。現在它工作正常。 – futureelite7 2009-11-17 03:35:02

0

我相信,當你說你是輪詢非阻塞模式你指的是插座您使用的是「標準」 Selector.select()的方法呢?

當select返回,並表示有可從套接字讀取你應該只讀那些重新進入呼叫之前可供選擇字節的數據()。如果read()返回-1,則表明沒有更多字節可用於在緩衝區中立即讀取 - 這並不意味着套接字已關閉。因此我懷疑你在返回之前試圖完全填充緩衝區是不正確的。即使它確實起作用,您的I/O線程也會在數據到達時持續旋轉。特別是,它看起來像你只是忽略-1的返回值。

考慮重新架構代碼中使用一個有限狀態機的方法。例如,我在過去使用三態模型實現了它:IDLE,READ_MESSAGE_LENGTH和READ_MESSAGE。

+0

由於每個線程只有一個套接字,我不知道是否需要使用Selector.Select()。 在任何情況下,-1意味着流的結束達到=應通知父。我現在只是忽略了處理。 我會嘗試使用一個普通的Socket(),看看它是怎麼回事。 – futureelite7 2009-11-17 01:26:20