我正在尝试制作一个简单的网络客户端。客户端应该能够写入队列(缓冲区),第二个线程应该使用此缓冲区并将其写入服务器。
我使用java.nio
进行了尝试,并使用静态Thread
创建了ByteBuffer
。此ByteBuffer
用于while(true)
Thread
用于写入频道。
在我的主循环中,我正在通过put()
方法将一些Bytes放入静态缓冲区。
在调试模式下,我暂停了通道写入线程,然后通过我的主程序循环填充缓冲区(只需按下'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());
//}
}
[...]
My Thread命名为“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,所以我在没有框架的情况下尝试它)
答案 0 :(得分: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 */
}
您可能还需要根据队列是否为空来设置/删除频道上的写入兴趣。
答案 1 :(得分:1)
不是您问题的直接答案,但您应该考虑使用现有的NIO框架来简化这一过程。 Netty和Grizzly是很受欢迎的例子。我会个人使用Netty,而不是使用NIO从头开始编写我自己的服务器。
你可能还可以看看Netty如何处理缓冲区的读/写,因为我认为它们已经优化了它们的实现。
答案 2 :(得分:0)
NIO的重点是你不需要单独的线程。做填充的线程也应该写作。