Problema con Twisted Python: invio di dati binari
Domanda
Quello che sto cercando di fare è abbastanza semplice: inviare un file dal client al server. Innanzitutto, il client invia informazioni sul file, ovvero la sua dimensione. Quindi invia il file effettivo.
Questo è quello che ho fatto finora:
Server.py
from twisted.internet import reactor, protocol
from twisted.protocols.basic import LineReceiver
import pickle
import sys
class Echo(LineReceiver):
def connectionMade(self):
self.factory.clients.append(self)
self.setRawMode()
def connectionLost(self, reason):
self.factory.clients.remove(self)
def lineReceived(self, data):
print "line", data
def rawDataReceived(self, data):
try:
obj = pickle.loads(data)
print obj
except:
print data
#self.transport.write("wa2")
def main():
"""This runs the protocol on port 8000"""
factory = protocol.ServerFactory()
factory.protocol = Echo
factory.clients = []
reactor.listenTCP(8000,factory)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
Client.py
import pickle
from twisted.internet import reactor, protocol
import time
import os.path
from twisted.protocols.basic import LineReceiver
class EchoClient(LineReceiver):
def connectionMade(self):
file = "some file that is a couple of megs"
filesize = os.path.getsize(file)
self.sendLine(pickle.dumps({"size":filesize}))
f = open(file, "rb")
contents = f.read()
print contents[:20]
self.sendLine(contents[:20])
f.close()
# self.sendLine("hej")
# self.sendLine("wa")
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
# this connects the protocol to a server runing on port 8000
def main():
f = EchoFactory()
reactor.connectTCP("localhost", 8000, f)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
Il server produrrà solo l'oggetto deserializzato:
{'size': 183574528L}
Come mai? Cosa è successo ai 20 caratteri del file che volevo inviare?
Se usi " hej " e "wa" invia invece, li avrò entrambi (nello stesso messaggio, non due volte).
Qualcuno?
Soluzione
Hai impostato il tuo server in modalità raw con setRawMode (), quindi il callback rawDataReceived viene chiamato con i dati in arrivo (non lineReceived). Se stampi i dati che ricevi in ??rawDataReceived, vedi tutto incluso il contenuto del file, ma mentre chiami pickle per deserializzare i dati, viene ignorato.
O cambi il modo in cui invii i dati al server (suggerirei il formato netstring) o passi il contenuto all'interno dell'oggetto serializzato pickle e lo fai in una sola chiamata.
self.sendLine(pickle.dumps({"size":filesize, 'content': contents[:20]}))