UDP非阻塞还是单独的线程来接收?


10

我正在创建一个多人游戏(适用于64位以下的玩家)。我已经决定为网络循环设置一个单独的线程,但是我想知道是否最好创建一个额外的线程来接收UDP或将接收套接字设置为非阻塞(没有额外的线程)。

还是使用其他方法(例如异步套接字)更好?始终欢迎更好的方法!

Answers:


6

好的,首先,如果您有东西并且它正在工作,那么通常将其保留为一个好主意。为什么要修复未损坏的东西?

但是,如果您遇到麻烦,并且真的想重写您的网络代码,我认为您有四个主要选择:

  1. 多线程阻塞代码(您现在正在做什么)
  2. 具有阻塞触发通知的非阻塞套接字
  3. 具有就绪状态更改通知的非阻塞套接字
  4. 异步插座

我编写了许多多人游戏(从对等网络到大型多人游戏)客户端和服务器,我想认为选项2可以使游戏的服务器和客户端部分的复杂性降到最低,并具有相当好的性能。紧接着,我会选择选项4,但这通常需要您重新考虑整个程序,而我主要发现它对服务器而非客户端很有用。

特别是,我建议不要在多线程环境中阻塞套接字,因为这样做通常会导致锁定和其他同步功能,这不仅大大增加了代码的复杂性,而且还因为某些线程等待而降低了其性能。其他。

但最重要的是,大多数套接字实现(以及当时的大多数I / O实现)在最低级别都是非阻塞的。只是提供了阻塞操作以简化琐碎程序的开发。当套接字阻塞时,该线程中的CPU完全处于空闲状态,那么为什么要针对已经存在的非阻塞任务在阻塞抽象之上构建非阻塞抽象呢?

如果您没有尝试过非阻塞套接字编程,那会有些令人生畏,但是事实证明,这很简单,而且如果您已经在进行输入轮询,那么您就有了进行非阻塞套接字的思路。

您要做的第一件事是将套接字设置为非阻塞。您可以使用fcntl()

在此之后,在此之前send()recv()sendto()recvfrom()accept()connect()有点不同)或其他调用可能阻塞线程,您拨打select()的插座上。select()告诉您是否可以在不阻塞的情况下对套接字执行后续的读取或写入操作。在这种情况下,您可以安全地执行所需的操作,并且套接字不会阻塞。

将其包含在游戏中非常简单。如果您已经有一个游戏循环,例如:

while game_is_running do
    poll_input()
    update_world()
    do_sounds()
    draw_world()
end

您可以将其更改为如下所示:

while game_is_running do
    poll_input()
    read_network()
    update_world()
    do_sounds()
    write_network()
    draw_world()
end

哪里

function read_network()
    while select(socket, READ) do
        game.net_input.enqueue(recv(socket))
    end
end

function write_network()
    while not game.net_output.empty and select(socket, WRITE) do
        send(socket, game.net_output.dequeue())
    end
end

在资源方面,我认为每个人都必须在书架上拥有的唯一一本书,即使这是唯一的一本书,也就是已故的Richard Stevens 撰写的“ Unix网络编程,第1卷 ”。是否执行Windows或其他OS或语言套接字编程都没有关系。阅读本书之前,请不要以为您了解套接字。

在这里您可以找到可用的解决方案在多个套接字编程方面的总体概述(主要用于服务器编程相关的)另一个资源是这个页面


我刚刚启动了游戏引擎,因此重写不是问题。这个答案非常有用,感谢您的推荐书。您还知道一本更具体的游戏网络书籍吗?
Yannick Lange 2013年

3
@YannickLange:不,但是我不建议您去寻找特定于游戏的书,因为联网在很大程度上与类型无关,而且没有其他书能达到史蒂文斯的书水平。但是,在编写面向消息的程序(如大多数游戏)时,使用面向消息的协议是一个好主意,因为它使用UDP。许多人倾向于最终通过TCP编写消息抽象,它本身就是基于面向消息的协议(IP)构建的面向流的抽象。
2013年

-1

您没有编写使用哪种编程语言,但是大多数语言提供了良好的异步套接字框架,这些框架经常利用底层操作系统的功能。

当您基于阻塞套接字编写自己的线程实现时(请记住:当您有多个客户端时,每个套接字都需要一个线程),您将重新发明异步套接字框架已经提供的功能。

因此,我建议您使用异步套接字。


4
为什么每个插槽都需要一个线程?特别是异步套接字?许多重量级服务器使用“线程池”模型来处理套接字数据,其中每个线程为N个客户端提供服务,并根据需要生成或杀死它们。只是认为此答案需要改进:D
Grimshaw,2013年

@Grimshaw我认为您误解了我的答案。我想我现在已经阐明,使用阻塞套接字时将需要多线程模型。
菲利普

@Philipp我没有告诉我使用哪种编程语言,因为我认为它与问题并没有真正的关系(顺便说一下,我使用c ++)。感谢您提出使用异步套接字的建议。您是否知道一本推荐的书籍/网页,用于学习游戏(引擎)中的这些方法?
Yannick Lange 2013年

@Grimshaw:每个线程如何服务N客户端?如果要阻塞,并且要确保每个套接字都处理完其数据,而不管其余套接字的状态如何,则每个套接字必须有一个线程。那,或者不要使用阻塞套接字。
2013年

我的评论是对答案的第一版,与此同时,您对它进行了编辑和更正,忘了我说的话:D
Grimshaw 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.