J'essaie d'envoyer de grands tableaux d'octets d'une classe Protobuf d'un client Java au serveur Python. Cependant, ils ont une variable longueur, car parfois j'envoie les octets d'un objet depuis ClassA et parfois depuis ClassB.

J'ai un serveur de socket Python avec le code suivant dans la fonction qui écoute le socket:

byte_array = bytearray()

# receive the data in small chunks and print it
while True:
    data = connection.recv(64)
    if data:
        # output received data
        logger.debug("Data: %s" % data)
        byte_array.extend(data)

    else:
        # no more data -- quit the loop
        logger.debug("no more data.")
        break

logger.info("Generating response...")
connection.send(generate_response(byte_array))
logger.info("Sent response.")

J'assemble le grand tableau d'octets que je reçois en rassemblant les 64 octets au fur et à mesure que je les reçois.

Cependant, lorsque le tableau d'octets est entièrement transmis et qu'il n'y a plus rien à envoyer, le serveur se bloque sur la ligne connection.recv.

J'ai lu que c'est parce que recv se bloque jusqu'à ce qu'il reçoive quelque chose ou que la connexion soit fermée. Cependant, je ne veux pas fermer la connexion car je veux renvoyer ma réponse au client après avoir traité l'ensemble du tableau d'octets.

Je veux savoir quand le tableau d'octets que je reçois a été entièrement transmis , afin de pouvoir éviter ce blocage.

Je peux penser à trois options:

  • Définissez un octet "de fin" prédéfini, délimitant la fin du tableau d'octets.
  • Envoyez la taille du tableau d'octets au préalable puis au lieu de while True j'ai un cycle while bytes_read < expected_bytes.
  • Définissez un délai d'expiration sur la connexion et je suppose que lorsqu'un délai d'expiration se produit, cela signifie que tout a déjà été envoyé.

Je suis enclin à la première option, mais je ne sais pas quel caractère je dois utiliser pour terminer le tableau d'octets ni comment le lire dans mon code Python.

Aucune suggestion?

Merci.

2
jazzchipc 19 mars 2019 à 15:41

2 réponses

Meilleure réponse

Option 1:

Ainsi, pour la première option, vous pouvez définir un octet de fin qui n'apparaîtra nulle part dans votre message réel. Vous pouvez créer une chaîne pour par exemple "END" et la convertir en tableau d'octets et l'envoyer via votre programme java. Après réception, vous pouvez utiliser decode () pour le convertir en chaîne et le comparer. :

Remarque: l'octet de fin que vous enverrez doit être inférieur ou égal à la taille du bloc à décoder et obtenir l'octet de fin exact.

byte_array = bytearray()

# receive the data in small chunks and print it
while True:
    data = connection.recv(64)
    command = data.decode()
    if command != "END":
        # output received data
        logger.debug("Data: %s" % data)
        byte_array.extend(data)

    else:
        # no more data -- quit the loop
        logger.debug("no more data.")
        break

logger.info("Generating response...")
connection.send(generate_response(byte_array))
logger.info("Sent response.")

Option 2:

Pour la deuxième option, vous devrez modifier la boucle while pour qu'elle s'exécute en fonction des métadonnées. J'ai considéré que les métadonnées seront constituées du premier bloc qui sera le nombre de blocs qui seront envoyés. Cela pourrait aller quelque chose comme:

Byte_array = bytearray ()

# receive the data in small chunks and print it
loop_count = 0
count = 1
meta = 1
while loop_count >= count:
    data = connection.recv(64)
    if(meta):
        count = int(data.decode()) # first chunk is the number of chunks that will be sent 
        meta = 0
    logger.debug("Data: %s" % data)
    byte_array.extend(data)
    loop_count = loop_count + 1
else:
    # no more data
    logger.debug("no more data.")
logger.info("Generating response...")
connection.send(generate_response(byte_array))
logger.info("Sent response.")

Option 3:

Cela fonctionnera également très bien à condition que vous soyez sûr qu'il n'y aura pas de retard réseau et que le seul problème sera que votre programme java devra attendre la réponse du serveur python jusqu'à l'expiration du délai.

Option 4:

Vous pouvez utiliser une prise non bloquante qui fonctionnera jusqu'à ce qu'elle ne soit pas reçue pendant une période de temps prédéterminée. Bien que je ne le recommande pas pour votre situation, vous pouvez le lire et voir s'il convient à vos besoins.

1
Community 20 juin 2020 à 09:12

Je choisirais personnellement la deuxième option (combinée à un délai raisonnable pour répondre aux clients malveillants qui n'envoient que la moitié du fichier et y restent pour toujours). Délimiter le caractère est bon si vous pouvez absolument garantir qu'il est unique dans votre flux (mais vous avez toujours besoin du délai).

Si vous ne pouvez pas garantir que votre délimiteur est unique, l'envoi de la taille attendue par le client résout le problème. Si vos métadonnées sont remplies sur une longueur fixe, vous n'avez pas à vous soucier des délimiteurs et à les détecter.

3
Hannu 19 mars 2019 à 15:08