Lorsque j'essaie de recevoir de plus grandes quantités de données, elles sont coupées et je dois appuyer sur Entrée pour obtenir le reste des données. Au début, je pouvais l'augmenter un peu, mais il ne recevrait toujours pas tout. Comme vous pouvez le voir, j'ai augmenté la mémoire tampon sur la conn.recv (), mais toutes les données ne sont toujours pas reçues. Cela le coupe à un moment donné. Je dois appuyer sur Entrée sur mon raw_input afin de recevoir le reste des données. Est-ce que je peux quand même obtenir toutes les données en même temps? Voici le code.
port = 7777
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', port))
sock.listen(1)
print ("Listening on port: "+str(port))
while 1:
conn, sock_addr = sock.accept()
print "accepted connection from", sock_addr
while 1:
command = raw_input('Shell> ')
conn.send(command)
data = conn.recv(8000)
if not data: break
print data,
conn.close()
TCP/IP est un protocole basé sur le flux, pas un protocole basé sur un message Rien ne garantit que chaque appel send()
par un homologue entraîne un seul appel recv()
par l'autre homologue recevant les données exactes envoyées. Il pourrait recevoir les données fragmentées, réparties en plusieurs appels recv()
, en raison de la fragmentation des paquets.
Vous devez définir votre propre protocole basé sur des messages en plus de TCP afin de différencier les limites des messages. Ensuite, pour lire un message, vous continuez à appeler recv()
jusqu'à ce que vous ayez lu l'intégralité du message ou qu'une erreur se produise.
Un moyen simple d’envoyer un message consiste à préfixer chaque message par sa longueur. Ensuite, pour lire un message, vous devez d'abord lire la longueur, puis plusieurs octets. Voici comment vous pouvez le faire:
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
Ensuite, vous pouvez utiliser les fonctions send_msg
et recv_msg
pour envoyer et recevoir des messages entiers. Ils n'auront aucun problème avec la division ou la fusion des paquets au niveau du réseau.
Vous pouvez l'utiliser comme suit: data = recvall(sock)
def recvall(sock):
BUFF_SIZE = 4096 # 4 KiB
data = b''
while True:
part = sock.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
# either 0 or end of data
break
return data
La réponse acceptée est acceptable mais elle sera très lente avec les gros fichiers. -String est une classe immuable. Cela signifie que plus d'objets sont créés chaque fois que vous utilisez le signe +
. Utiliser list
comme structure de pile sera plus efficace.
Cela devrait mieux fonctionner
while True:
chunk = s.recv(10000)
if not chunk:
break
fragments.append(chunk)
print "".join(fragments)
Vous devrez peut-être appeler conn.recv () plusieurs fois pour recevoir toutes les données. L'appel d'une seule fois n'est pas garanti pour importer toutes les données envoyées, car les flux TCP ne conservent pas les limites des trames (c'est-à-dire qu'ils fonctionnent uniquement en tant que flux d'octets bruts, et non structurés. flux de messages).
Voir cette réponse pour une autre description du problème.
Notez que cela signifie que vous avez besoin d’un moyen de savoir quand vous avez reçu toutes les données. Si l'expéditeur envoie toujours exactement 8 000 octets, vous pouvez compter le nombre d'octets que vous avez reçus jusqu'à présent et le soustraire de 8 000 pour savoir combien il en reste à recevoir. Si les données sont de taille variable, il existe diverses autres méthodes pouvant être utilisées, telles que l'envoi par l'en-tête d'un en-tête de nombre d'octets avant l'envoi du message ou le texte ASCII qui vous est envoyé. pourrait rechercher un caractère de nouvelle ligne ou NUL.
Une variante utilisant une fonction génératrice (que je considère plus pythonique):
def recvall(sock, buffer_size=4096):
buf = sock.recv(buffer_size)
while buf:
yield buf
if len(buf) < buffer_size: break
buf = sock.recv(buffer_size)
# ...
with socket.create_connection((Host, port)) as sock:
sock.sendall(command)
response = b''.join(recvall(sock))
Modifier le code d'Adam Rosenfield:
import sys
def send_msg(sock, msg):
size_of_package = sys.getsizeof(msg)
package = str(size_of_package)+":"+ msg #Create our package size,":",message
sock.sendall(package)
def recv_msg(sock):
try:
header = sock.recv(2)#Magic, small number to begin with.
while ":" not in header:
header += sock.recv(2) #Keep looping, picking up two bytes each time
size_of_package, separator, message_fragment = header.partition(":")
message = sock.recv(int(size_of_package))
full_message = message_fragment + message
return full_message
except OverflowError:
return "OverflowError."
except:
print "Unexpected error:", sys.exc_info()[0]
raise
J'encouragerais toutefois fortement l'utilisation de l'approche originale.
Vous pouvez le faire en utilisant la sérialisation
from socket import *
from json import dumps, loads
def recvall(conn):
data = ""
while True:
try:
data = conn.recv(1024)
return json.loads(data)
except ValueError:
continue
def sendall(conn):
conn.sendall(json.dumps(data))
NOTE: Si vous voulez utiliser un fichier avec le code ci-dessus, vous devez l’encoder/le décoder en base64.