2012-01-06 145 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