我正在创建一个多人游戏(适用于64位以下的玩家)。我已经决定为网络循环设置一个单独的线程,但是我想知道是否最好创建一个额外的线程来接收UDP或将接收套接字设置为非阻塞(没有额外的线程)。
还是使用其他方法(例如异步套接字)更好?始终欢迎更好的方法!
我正在创建一个多人游戏(适用于64位以下的玩家)。我已经决定为网络循环设置一个单独的线程,但是我想知道是否最好创建一个额外的线程来接收UDP或将接收套接字设置为非阻塞(没有额外的线程)。
还是使用其他方法(例如异步套接字)更好?始终欢迎更好的方法!
Answers:
好的,首先,如果您有东西并且它正在工作,那么通常将其保留为一个好主意。为什么要修复未损坏的东西?
但是,如果您遇到麻烦,并且真的想重写您的网络代码,我认为您有四个主要选择:
我编写了许多多人游戏(从对等网络到大型多人游戏)客户端和服务器,我想认为选项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或语言套接字编程都没有关系。阅读本书之前,请不要以为您了解套接字。
在这里您可以找到可用的解决方案在多个套接字编程方面的总体概述(主要用于服务器编程相关的)另一个资源是这个页面。
您没有编写使用哪种编程语言,但是大多数语言提供了良好的异步套接字框架,这些框架经常利用底层操作系统的功能。
当您基于阻塞套接字编写自己的线程实现时(请记住:当您有多个客户端时,每个套接字都需要一个线程),您将重新发明异步套接字框架已经提供的功能。
因此,我建议您使用异步套接字。
N
客户端?如果要阻塞,并且要确保每个套接字都处理完其数据,而不管其余套接字的状态如何,则每个套接字必须有一个线程。那,或者不要使用阻塞套接字。