问题
I'm trying to develop simple TCP
, clinet/server game using Twisted and Pygame, but I have difficulties with sending data to clients. Twisted doesn't allow me to send multiple responses in a row. That's what I'm trying to do:
I have method witch handle player status changes and resend them to other clients:
def handle_stateChanged(self, data):
#get playerState from client and override local copy of player
#check if all players are ready
#if needed, change gameState form 'inLOBBY' to 'inGAME'
#if gameState == 'inGAME', start 'proceed' method (see below)
#send message about player and game state to others
and proceed
method(called every 1s/30 using LoopingCall
) with simply calculate all game stuff and send it to players. Those two functions block each other, separately they work just fine, but when working together only data from one of them reach destination. Also something like this doesn't work:
def dataRecived(self, data):
...
sendData(data1) #only data1 is delivered
sendData(data2)
...
I don't know if it's the way TCP works or it's my lack of knowledge on Twisted. How should I send updates to clients and in background handle user input?
EDIT:
class Server(Protocol):
def __init__(self, factory):
self.factory = factory
self.player = None #when connection is made here goes my player class
self.world = factory.world
...
self.factory.loop = LoopingCall(self.proceed)
def dataReceived(self, data):
data = pickle.loads(data)
#this calls 'handle_stateChanged'
getattr(self, 'handle_{}'.format(data['header']))(data) #data[header] = 'stateChanged'
def handle_stateChanged(self, data):
isReady = data['isReady']
self.player.isReady = isReady
if isReady:
count = 0
for connection in self.factory.connections.values():
if connection.player.isReady:
count += 1
if count == len(self.factory.connections) and count > 1 and self.world.state == 'inLOBBY':
self.world.state = 'inGAME'
self.world.playersDistribution(self.factory.connections)
self.factory.loop.start(1 / 30)
data['state'] = self.world.state
data['players'] = self.getPlayers()
self.sendToOthers(data)
def sendToOthers(self, data, omitId = None):
connections = self.factory.connections
for key in connections.keys():
if key != omitId:
connections[key].sendData(data)
def proceed(self):
#It's only a prototype of my method.
#The point is that the client keep getting
#'test' and data from self.sendToOthers(data) in handle_stateChanged
#is not being delivered even if the method (handle_stateChanged) is called
if self.world.state != 'inGAME':
return
data = {'header' : 'message', 'body' : 'test'}
#When i comment this out, handle_stateChanged works fine and sends data
self.sendToOthers(data)
class ServerFactory(Factory):
def __init__(self, world):
self.world = world
self.connections = {}
def buildProtocol(self, addr):
return Server(self)
回答1:
Your server has a remote arbitrary code execution vulnerability in it.
There are very few - if any - circumstances under which you should unpickle data received from the network. Doing so allows any peer to hijack your server for arbitrary, perhaps malicious, purposes. Note the big red box in the pickle documentation.
Apart from this serious security issue, the problem you're having with only the first piece of data being sent being interpreted is probably caused by the two pieces of data being joined together as they traverse the network. Your receiving code has no proper framing support, so it can't tell there are two messages. It so happens that pickle will load data from the first message and ignore the extra data afterwards which represents the second message, effectively dropping that data on the floor.
Both the security problem and the framing problem can be solved if you switch to a more expressive protocol (more expressive than bare TCP transporting unframed pickle strings), for example AMP.
来源:https://stackoverflow.com/questions/14588131/multiple-responses-in-twisted