【问题标题】:Why should I use NIO for TCP multiplayer gaming instead of simple sockets (IO) - or: where is the block?为什么我应该将 NIOS 用于 TCP 多人游戏而不是简单的套接字 (IO) - 或者:块在哪里?
【发布时间】:2012-01-05 13:50:33
【问题描述】:

我正在尝试为 Android 设备创建一个简单的多人游戏。我一直在考虑网络代码,现在阅读了很多关于套接字的页面。 Android 应用程序将只是一个客户端,并且只连接到一个服务器。

由于“阻塞”,几乎在任何地方(这里也是)你都会得到使用 NIO 或使用 NIO 的框架的建议。 我试图了解一个简单的套接字实现的问题是什么,所以我创建了一个简单的测试来尝试一下:

我的主要应用:

[...]
Socket clientSocket = new Socket( "127.0.0.1", 2593 );
new Thread(new PacketReader(clientSocket)).start();
PrintStream os = new PrintStream( clientSocket.getOutputStream() );
os.println( "kakapipipopo" );
[...]

PacketReader 线程:

class PacketReader implements Runnable
{
    Socket m_Socket;
    BufferedReader m_Reader;

    PacketReader(Socket socket)
    {
        m_Reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    }

    public void run()
    {
        char[] buffer = new char[200];
        int count = 0;
        while(true)
        {
            count = m_Reader.read(buffer, 0, 200);
            String message = new String(buffer, 0, count);
            Gdx.app.log("keks", nachricht);
        }
    }
}

我无法遇到我应该遇到的阻塞问题。我认为 read() 函数会阻止我的应用程序,我什么也做不了——但一切都很好。 我一直在想:如果我只是在我的应用程序中创建一个输入和输出缓冲区并创建两个线程,它们将从我的两个缓冲区写入和读取套接字怎么办?这行得通吗? 如果是 - 为什么每个人都推荐 NIO?在正常 IO 方式中的某个地方必须发生块,但我找不到它。 使用 NIO 进行 Android 多人游戏还有其他好处吗?我认为 NIO 似乎更复杂,因此可能不太适合移动设备,但可能简单的套接字方式更适合移动设备。

如果有人能告诉我问题出在哪里,我会非常高兴。我不害怕 NIO,但至少我想知道我为什么要使用它 :D

问候

-托马斯

【问题讨论】:

  • 我看不出有任何理由在客户端使用 NIO,除非它要连接到数千台服务器。

标签: java android sockets nio multiplayer


【解决方案1】:

阻塞是,read() 将阻塞当前线程,直到它可以从套接字的输入流中读取数据。因此,您需要一个专用于该单个 TCP 连接的线程。

如果您有超过 10,000 个客户端设备与您的服务器连接,该怎么办?您需要至少 10k 个线程来处理所有客户端设备(假设每个设备都维护一个 TCP 连接),无论它们是否处于活动状态。即使只有 100 个处于活动状态,上下文切换和其他多线程的开销也会过多。

NIO 使用选择器模型来处理这些客户端,这意味着您不需要为每个 TCP 连接使用专用线程来接收数据。您只需要选择所有活动连接(已收到数据)并处理这些活动连接。您可以控制应该在服务器端维护多少线程。

【讨论】:

  • 他正在写一个客户端。
【解决方案2】:

编辑 这个答案有点不完全回答OP的要求。对于客户端来说很好,因为客户端将只连接到一台服务器。虽然我的回答给出了一些关于阻塞和非阻塞 IO 的通用概念。

我知道这个答案会在 3 年后出现,但这可能会对未来的人有所帮助。

在阻塞套接字模型中,如果数据不可读取或服务器尚未准备好写入,则网络线程将等待读取或写入套接字的请求,直到它获取或发送数据要么 超时。换句话说,如果程序无法继续,程序可能会在该点停止相当长的一段时间。为了取消这一点,我们可以为每个连接创建一个线程,它可以同时处理来自每个客户端的请求。如果选择这种方法,应用程序的可伸缩性可能会受到影响,因为在连接了数千个客户端的场景中,拥有数千个线程可能会占用系统的所有内存,因为创建线程的成本很高,并且会影响系统的性能。应用。

在非阻塞套接字模型中,对套接字的读取或写入请求无论是否成功都会立即返回,换句话说,异步返回。这使网络线程保持忙碌。然后我们的任务是决定是重试还是考虑读/写操作完成。这样就创建了一种事件驱动的通信方法,我们可以在需要时创建线程,从而形成一个更具可扩展性的系统。

下图解释了阻塞和非阻塞套接字模型之间的区别。

【讨论】:

  • 套接字几乎总是“准备好写入”。除非发送方的发送缓冲区和接收方的接收缓冲区都已满,否则网络线程不会阻塞写入,这表明发送方已大大超出接收方。异步 I/O 与非阻塞 I/O 不同。 C10K 谬误在十年或更长时间前就暴露了。那里有具有数十万个线程的服务器。无论如何,这无关紧要,因为他正在写一个客户端。
  • 感谢您解释缓冲区内容。不会同意您的数千条线程声明,因为我说的是它很糟糕。它不是最优的。我们不应该只是创建什么都不做的线程。我不是说它不可能。是的,对于客户来说,这肯定是无关紧要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
相关资源
最近更新 更多