检测 TCP 何时拥塞 python twisted socket 服务器

Detect when TCP is congested python twisted socket server

我正在开发一款实时 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)

@@@@@@@@@@@@@更新@@@@@@@@@@@@: 如何分别为每个协议实例实现拥塞检查?请从下面的代码示例中的内容开始:(其中显示 'HELP HERE PLEASE'!) 我在想这个错误吗?任何指导都会很棒,谢谢!

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()

You probably don't need UDP (yet).

你说的第一件事就是你想要"reduce network congestion ... from TCP"。这不是 UDP 所做的。 UDP 允许您绕过 拥塞控制 ,这实际上 增加了 网络拥塞。在您了解如何实施自己的拥塞控制算法之前,高延迟连接上的 UDP 流量只会导致数据包风暴,使您的服务器不堪重负并淹没用户的网络连接,使它们无法使用。

在实时游戏中发送移动数据包的重要一点是,您始终要确保在新位置已经可用时,您不会在旧移动数据包上浪费时间 "catching up"。在 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 将连接标记为需要发送位置更新。这会将协议注册为其传输的生产者,这将导致每次写入缓冲区 space 可用时调用 resumeProducing。一旦 resumeProducing 被调用,我们发送任何 latest 位置 - 如果 updatePosition 在网络拥塞时被调用 500 次,则只会更新一次拥塞缓解后立即发送。

这有点过于简单化了,因为每个 transport 一次只能有一个 producer,而且您的游戏服务器可能会有很多不同的位置更新要发送给客户端,所以您将需要一个多路复用器来聚合来自多个客户端的所有位置更新,还需要一些代码来排序消息,以便位置更新以外的内容仍然可以通过,但位置更新具有优先权。

如果您无论如何都要执行 UDP,这可能看起来像是额外的工作,但如果您要正确执行 UDP 并实际从中获得任何好处,您将需要实现非常像这样的东西 反正,这样就不浪费了