2012-01-06 161 views
1

我試圖做一個簡單的網絡客戶端。客戶端應該能夠寫入一個隊列(緩衝區),第二個線程應該把這個緩衝區寫入服務器。試圖填充緩衝區,同時緩衝區寫入通道

我用java.nio試了一下,並用靜態ByteBuffer創建了ThreadByteBuffer正用於Threadwhile(true)中,用於寫入通道。

在我的主循環中,我通過put()方法將一些字節放入靜態緩衝區。

在調試模式下,我暫停了通道寫入線程,然後通過主程序循環填充緩衝區(只需將'A'寫入緩衝區)。

在三個或四個按鈕按下後,我再次啓動通道寫入線程,它工作得很好。

但是,當我正常嘗試程序時,我在主循環線程中出現緩衝區溢出錯誤。我相信我的程序正試圖將數據放入緩衝區,同時通過我的通道寫入線程訪問緩衝區。我試圖在兩個線程的兩個部分中使用synchronized關鍵字,但這並沒有幫助。

主迴路:

[...] 
    if(Gdx.app.getInput().isKeyPressed(Input.Keys.A) && (now.getTime() - lastPush.getTime()) > 1000) 
     { 
      lastPush = now; 
      //synchronized (PacketReader.writeBuffer) 
      //{ 
       PacketReader.writeBuffer.put(("KeKe").getBytes()); 
      //} 
} 
[...] 

我的主題命名爲 「PacketReader」(以及它的實際讀,寫):

class PacketReader implements Runnable 
{ 
public static ByteBuffer writeBuffer = ByteBuffer.allocate(1024); 
[...] 
public void run() 
{ 
    while (true) { 
[...] 
     if (selKey.isValid() && selKey.isWritable()) 
     { 
      SocketChannel sChannel = (SocketChannel)selKey.channel(); 

      //synchronized (PacketReader.writeBuffer) 
      //{ 
       if(PacketReader.writeBuffer.hasRemaining()) 
       { 
        PacketReader.writeBuffer.flip(); 
        int numBytesWritten = sChannel.write(PacketReader.writeBuffer); 
        PacketReader.writeBuffer.flip(); 
       } 
      //} 
     } 
[...] 

任何想法如何創建這樣一個緩衝的寫入系統?我認爲這是一個常見問題,但我不知道要搜索什麼。所有的NIO教程似乎認爲緩衝區在通道循環中被填滿。

最後我試圖有一個程序,其中網絡組件啓動一次,並在我的程序中,我只是想使用一些靜態發送方法發送數據包,而不考慮隊列處理或等待隊列。

是否有可能在某個地方的教程?大多數遊戲應該使用類似的概念,但是我找不到任何帶有NIO實現的開源簡單java遊戲(我將使用它用於android,所以我在沒有框架的情況下使用它)

回答

1

不直接回答你的問題,但你應該考慮使用現有的NIO框架來使這更容易。 Netty和灰熊是很受歡迎的例子。我會個人使用Netty,而不是使用NIO從頭開始編寫自己的服務器。

你可能也可以看看Netty如何處理對緩衝區的讀/寫,因爲我假設他們已經優化了它們的實現。

+0

謝謝您的建議。我一直在考慮像Kryonet或Pyronet這樣的Lightwight框架,但是當我在Android上使用這個框架時,我認爲直接編程對性能會更好。另一方面,我想學習NIO,那麼爲什麼不折磨自己呢? :D – 2012-01-10 14:55:23

2

您可能會嘗試保留要寫入的緩衝區的隊列(例如,ConcurrentLinkedQueue),而不是放入要發送到通道的同一緩衝區中。

要排隊的東西發送:

ByteBuffer buff = /* get buffer to write in */; 
buff.put("KeKe".getBytes()); 
queue.add(buff); 

然後在你的選擇循環,當信道是可寫的:

for(ByteBuffer buff = queue.poll(); buff != null; buff = queue.poll()) { 
    sChannel.write(buff); 
    /* maybe recycle buff */ 
} 

您可能還需要設置/通道上刪除寫興趣取決於隊列是否爲空。

+0

謝謝羅素:)我用你的例子來申請我的應用程序。我仍然有關於你的解決方案的一些問題:這不意味着我正在創建大量的緩衝區(對於每個數據包發送?)?這可能會導致Android設備上的資源問題?或者我一直在考慮將byte []數組放入隊列並通過ByteArrayOutputStream創建字節數組。所以這裏的問題是:ByteArrayOutputStream vs ByteBuffer,Android設備最適合什麼? – 2012-01-10 15:10:07

0

NIO的重點在於您不需要單獨的線程。做填充的線程也應該寫。

+0

對不起,我完全不明白。我正在做一個小型的Android遊戲 - 根據你我應該做的全部填充和寫入渲染線程中的東西?在圖形和邏輯部分之前?但是如果從套接字發送或讀取花費很多時間呢?我將不得不等待渲染,因此遊戲會變得緩慢......如果我的渲染耗時太長,我的閱讀緩衝區是否有溢出的風險呢?或者更糟糕的是,我是否會丟失包裹? – 2012-01-10 14:53:52

+0

@ Thom-我沒有對渲染線程做任何說明。 – EJP 2012-02-04 09:13:22