2013-05-08 53 views
0

我有一臺服務器使用非阻塞套接字nio。服務器在單獨的線程中工作,並且有另一個稱爲遊戲的線程。遊戲線程保存服務器對象並使用server.sendMessage,服務器線程只讀取數據。當我在一個while循環中連續兩次調用sendMessage兩次數據包後,我在客戶端得到「java.io.StreamCorruptedException:invalid stream header:6B6574B4」錯誤。服務器代碼Sequentally Channel Write在Java.NIO中發送損壞的數據

部分:

public void write(SelectionKey channelKey, byte[] buffer) { 
    if (buffer != null) { 
     int bytesWritten; 
     try { 
      SocketChannel channel = (SocketChannel) channelKey.channel(); 
      synchronized (channel) { 
       bytesWritten = channel.write(ByteBuffer.wrap(buffer)); 
      } 
      if (bytesWritten == -1) { 
       resetKey(channelKey); 
       disconnected(channelKey); 
      } 
     } catch (Exception e) { 
      resetKey(channelKey); 
      disconnected(channelKey); 
     } 
    } 
} 

public void broadcast(byte[] buf, SelectionKey fr) { 
    synchronized (clientList) { 
     Iterator<SelectionKey> i = clientList.iterator(); 
     while (i.hasNext()) { 
      SelectionKey key = i.next(); 
      if (fr != key) 
       write(key, buf); 
     } 
    } 
} 

public synchronized void sendMessage(Packets pk) { 
    broadcast(pk.toByteArray(), null); 
} 

回答

1

我猜(從已包含的代碼量小),就是你不劃定您的郵件在所有。即使您分別發送2條消息,io層也可以以各種方式拆分/組合這些消息,以便接收者獲得附加到先前消息的消息的一部分。你應該使用某種「消息」協議向接收者指出要消耗多少字節,以便它可以正確解析每個傳入消息(例如先寫入消息字節長度,然後再寫入消息字節)。

作爲一個備註,write()方法不保證在一次調用中寫入所有字節,所以您應該處理返回值並根據需要寫入其餘字節。

+0

+1對於「循環」,但你應該提到循環不是字面上的while(!allWritten){write(lastPartOfBuf();}',它是異步操作鏈。 – 2013-05-09 01:00:50

0

您需要在寫入之前flip()以及之後的compact(),並且您需要停止假設一個write()寫入整個緩衝區。它返回一個值的原因。您需要循環,或者如果您處於非阻塞模式,則需要按以下步驟進行操作:

  1. 寫。
  2. 如果寫入未完全完成,請爲OP_WRITE註冊通道並返回到選擇循環。
  3. 當通道變爲可寫時,再次嘗試寫入,如果仍然沒有完成,只保持循環。
  4. 否則取消註冊OP_WRITE。