检测 TCP 何时拥塞 python 扭曲套接字服务器
Posted
技术标签:
【中文标题】检测 TCP 何时拥塞 python 扭曲套接字服务器【英文标题】:Detect when TCP is congested python twisted socket server 【发布时间】:2015-08-17 05:38:17 【问题描述】:我正在开发一款实时 MMO 游戏,并且有一个可用的 TCP 服务器(以及游戏客户端),但现在我正在考虑使用 UDP 不断更新其他玩家的位置(大大从 TCP 拥塞控制中减少随机游戏填充器!) 我希望在这方面比我更聪明的人提供一些帮助(我是 python/twisted 新手,在其他地方找不到此信息;))
目前,我的服务器接受使用简单 Twisted 协议的连接。例如。
''' TCP reciever '''
class TCPProtocol(Protocol):
def connectionMade(self):
#add to list of connected clients
factory.clients.append(self)
def dataReceived(self, data):
pass
#setup factory and TCP protocol class
factory = Factory()
factory.protocol = TCPProtocol
factory.clients = []
reactor.listenTCP(1959, factory)
@@@@@@@@@@@@@@ 更新@@@@@@@@@@@@@ : 如何分别为每个协议实例实现拥塞检查?请在下面的代码示例中开始我的一些事情:(上面写着“请帮助”!) 我想错了吗?任何指导都会很棒,谢谢!
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
KServerIP='127.0.0.1'
KServerPortTCP=8000
#KClientPortUDP=7055
''' TCP reciever '''
class TCPProtocol(Protocol):
def connectionMade(self):
#add to list of connected clients
factory.clients.append(self)
#set client variables (simplified)
self.pos_x=100
self.pos_y=100
#add to game room (to recieve updates)
mainGameRoom.clientsInRoom.append(self)
def dataReceived(self, data):
pass
#send custom byte message to client (I have special class to read it)
def sendMessageToClient(self, message, isUpdate):
''' @@@@@@@@@@@@@@@@@ HELP HERE PLEASE! @@@@@@@@@@@
if isUpdate and (CHECK IF CLIENT IS CONGESTED??? )
return (and send next update when not congested)'''
'''@@@@@@@@@@@@@@@@@ HELP HERE PLEASE! @@@@@@@@@@@ '''
#if not congested, write update!
msgLen = pack('!I', len(message.data))
self.transport.write(msgLen) #add length before message
self.transport.write(message.data)
#simplified version of my game room
#this room runs the game, and clients recieve pos updates for
#everyone in this room (up to 50 people)
dt_gameloop=1.0/60 #loop time difference
dt_sendClientUpdate=0.1 #update intervar
class GameRoom(object):
#room constants
room_w=1000
room_h=1000
def __init__(self):
super(GameRoom, self).__init__()
#schedule main game loop
l=task.LoopingCall(self.gameLoop)
l.start(dt_gameloop) # call every X seconds
#schedule users update loop
l=task.LoopingCall(self.sendAllClientsPosUpdate)
l.start(dt_sendClientUpdate) # call every X seconds
def gameLoop(self):
#game logic runs here (60 times a second), EG.
for anUpdateClient in self.clientsInRoom:
anUpdateClient.pos_x+=10
anUpdateClient.pos_y+=10
#send position update every 0.1 seconds,
#send all player positions to all clients
def sendAllClientsPosUpdate(self):
message = MessageWriter()
message.writeByte(MessageGameLoopUpdate) #message type
#create one byte message containing info for all players
message.writeInt(len(self.clientsInRoom)) #num items to read
for aClient in self.clientsInRoom:
message.writeInt(aClient.ID)
message.writeFloat( aCell.pos_x )#pos
message.writeFloat( aCell.pos_y )
#send message to all clients
for aClient in self.clientsInRoom:
aClient.sendMessageToClient(message, True)
#setup factory and TCP protocol class
factory = Factory()
factory.protocol = TCPProtocol
factory.clients = []
reactor.listenTCP(KServerPortTCP, factory)
#simple example, one game room
mainGameRoom=GameRoom()
print "Server started..."
reactor.run()
【问题讨论】:
所以你正在尝试向客户端发送 UDP 消息? 简短回答:getpeername -- 但我强烈建议您暂时忽略 UDP,并首先使用 TCP 让一切正常工作。 首先您需要认真考虑哪些数据需要何时由谁发送 i> where(又名更高级别的游戏协议)。如果您已经遇到拥塞问题,那么切换到 UDP 将无法挽救您。如果你没有问题,先做一个干净和精益的参考实现,然后再考虑优化。 感谢您的回复。我的游戏几乎完成了,它可以使用 TCP,但是在 wifi 上玩它时(即使连接速度很快),每隔几秒就会卡顿,持续 0.5-3 秒,这使得它的游戏体验很糟糕。我已经考虑过进行拥塞检查,在添加 UDP 之前,请检查更新的代码示例(问题的底部,帮助我:) 更新,我已经成功地实现了 UDP 作为第二个连接,与第一个(暂时)无关。我已经制作了一个客户端系统和心跳检查。 【参考方案1】:You probably don't need UDP (yet).
你说的第一件事是你想“减少网络拥塞......来自 TCP”。这不是 UDP 所做的。 UDP 允许您解决拥塞控制,这实际上增加网络拥塞。在您了解如何实现自己的拥塞控制算法之前,高延迟连接上的 UDP 流量只会导致数据包风暴,从而使您的服务器不堪重负并淹没您用户的网络连接,使它们无法使用。
在实时游戏中发送移动数据包的重要一点是,您始终希望确保在新位置已经可用时不会浪费时间“追赶”旧的移动数据包。在 Twisted 中,您可以使用 producer and consumer APIs 在 TCP 连接上执行此操作,如下所示:
from zope.interface import implementer
from twisted.internet.protocol import Protocol
from twisted.internet.interfaces import IPullProducer
def serializePosition(position):
"... take a 'position', return some bytes ..."
@implementer(IPullProducer)
class MovementUpdater(Protocol, object):
def updatePosition(self, newPosition):
if newPosition != self.currentPosition:
self.currentPosition = newPosition
self.needToSendPosition()
waitingToSend = False
def needToSendPosition(self):
if not self.waitingToSend:
self.waitingToSend = True
self.transport.registerProducer(self, False)
def resumeProducing(self):
self.transport.write(serializePosition(self.currentPosition))
self.transport.unregisterProducer()
self.waitingToSend = False
def stopProducing(self):
"nothing to do here"
游戏每次需要发送新位置时,可以调用updatePosition
更新玩家当前位置。 updatePosition
首先更新当前位置,然后调用needToSendPosition
将连接标记为需要发送位置更新。这会将协议注册为其传输的生产者,这将导致每次写入缓冲区空间可用时调用resumeProducing
。只要resumeProducing
被调用,我们就会发送任何最新 位置的信息 - 如果在网络拥塞时updatePosition
被调用 500 次,一旦拥塞缓解,只会发送一个更新.
这有点过于简单了,因为每个transport
一次只能有一个producer
,并且您的游戏服务器可能有很多不同的位置更新要发送给客户端,因此您需要一个多路复用器来聚合来自多个客户端的所有位置更新,以及一些用于排序消息的代码,以便位置更新以外的内容仍然可以通过,但位置更新具有优先权。
如果您仍然要使用 UDP,这可能看起来像是额外的工作,但如果您要正确使用 UDP 并从中获得任何好处,您将需要实现非常类似的东西 无论如何,所以这不会浪费。
【讨论】:
感谢您的回答。我的游戏中的问题来自于持续 1-2 秒的卡顿(来自 TCP 拥塞控制)。我考虑过在 TCP 中创建一个智能系统,它只在没有拥塞时发送位置更新,但除了心跳返回消息之外,还没有弄清楚如何去做(对于每个收到的更新,客户端发回一条消息,如果一段时间内没有消息,则停止发送更新)如果每个协议都可以有一种方法来检查它是否拥塞,那就太好了,你能帮我解决这个问题吗? (我已经用细节更新了问题) 请记住,如果您因拥塞控制而出现卡顿,则可能会丢失数据包。如果数据包丢失,那么您也会在 UDP 上看到 一些 卡顿,尽管它的表现可能略有不同,因为数据包仍然会丢失并且不会重新传输。 您不希望有“检查是否拥塞”的方法,因为即使连接拥塞(即缺少确认导致背压),您仍然需要发送更新.如果你只是在拥塞时不发送,那么角色会出现在错误的位置,并且在拥塞消失之前不会更新,并且它们也会移动一点以强制更新。 所以,“在没有拥塞时发送更新”正是我上面的示例向您展示的。 更新:我最终将 UDP 和 TCP 一起使用,而 UDP 用于不断的游戏更新。最后工作得很好,我已经发布了我的游戏:itunes.apple.com/us/app/blewp!-eat-or-be-eaten-mmo/…以上是关于检测 TCP 何时拥塞 python 扭曲套接字服务器的主要内容,如果未能解决你的问题,请参考以下文章