J'essaie actuellement de contrôler une barrette d'alimentation intelligente à l'aide d'un script python. Pour ce faire, j'utilise une connexion TCP avec le module socket. Environ 75% du temps, j'obtiens la réponse / les données que je recherchais et tout fonctionne parfaitement. Cependant, environ 25% du temps, la réponse est coupée exactement à la même longueur, 1024 octets. Cela n'a aucun sens pour moi, car ma taille de tampon est en fait définie sur 2048 octets. La vitesse à laquelle j'attends entre l'utilisation de recv () ne semble pas non plus affecter / provoquer cela. Bien que TCP soit un flux d'octets, est-il toujours possible que cela ait à voir avec la fragmentation des paquets?
Code principal
def decrypt(string): key = 171 result = "" for i in string: a = key ^ ord(i) key = ord(i) result += chr(a) return result
Fonction de chiffrement
def encrypt(string): key = 171 result = pack('>I', len(string)) for i in string: a = key ^ ord(i) key = a result += chr(a) return result
Fonction de déchiffrement strong >
ip='192.168.0.62' port=9999 sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_tcp.connect((ip, port)) sock_tcp.send(encrypt('{"system":{"get_sysinfo":{}}}')) data = sock_tcp.recv(2048) sock_tcp.close() print len(data) #On succesful runs output is 1221, on unsuccesful runs it is 1024 rec = decrypt(data[4:]) print str(rec) #See output below
La chaîne elle-même que je reçois. Ce n'est probablement pas pertinent, mais j'ai pensé que je l'inclurais quand même. Il s'agit de la valeur de la variable rec.
Sortie souhaitée et régulière
Sortie souhaitée complète
{"system": {"get_sysinfo": {"sw_ver": "1.0.6 Build 180627 Rel.081000 "," hw_ver ":" 1.0 "," model ":" HS300 (États-Unis) "," deviceId ":" 80067B24A755F99C4D6C1807455E09F91AB7B2AA "," oemId ":" 5C9E6254BEBAED63B2B610 "" 5C9E6254BEBAED63B2B610 "" 5C9E6254BEBAED63B2B610 "2966D24817C47C47" ": -60," longitude_i ": - 1222955," latitude_i ": 379078," alias ":" TP-LINK_Power Strip_4F01 "," mic_type ":" IOT.SMARTPLUGSWITCH "," feature ":" TIM: ENE "," mac ":" B0: BE: 76: 12: 4F: 01 "," mise à jour ": 0," led_off " : 0, "children": [{"id": "80067B24A755F99C4D6C1807455E09F91AB7B2AA00", "state": 0, "alias": "CezHeat", "on_time": 0, "next_action": {"type": - 1}} , {"id": "80067B24A755F99C4D6C1807455E09F91AB7B2AA01", "state": 1, "alias": "CezUVB", "on_time": 191208, "next_action": {"type": - 1}}, {"id": " 80067B24A755F99C4D6C1807455E09F91AB7B2AA02 "," state ": 1," alias ":" CyanHeat "," on_time ": 191208," next_action ": {" type ": - 1}}, {" id ":" 80067B24A7505F99C4D6C1 " 1, "alias": "ZanderHeat", "on_time": 191208, "next_action": {"type": - 1}}, {"id": "80067B24A755F99C4D6C1807455E09F91AB7B2AA04", "state": 1, "alias": " CairoHeat "," on_time ": 191208," next_action ": {" type ": - 1}}, {" id ":" 80067B24A755F99C4D6C1807455E09F91AB7B2AA05 "," state ": 1," alias ":" KodaMister "," on_time ": 191208, "next_action": {"type": - 1}}], "child_num": 6, "err_code": 0}}}
Sortie anormale et plus rare
Sortie coupée
{"system": {"get_sysinfo": {"sw_ver": "1.0.6 Build 180627 Rel.081000 "," hw_ver ":" 1.0 "," model ":" HS300 (États-Unis) "," deviceId ":" 80067B24A755F99C4D6C1807455E09F91AB7B2AA "," oemId ":" 5C9E6254BEBAED63B2B610 "" 5C9E6254BEBAED63B2B610 "" 5C9E6254BEBAED63B2B610 "2966D24817" ": -59," longitude_i ": - 1222955," latitude_i ": 379078," alias ":" TP-LINK_Power Strip_4F01 "," mic_type ":" IOT.SMARTPLUGSWITCH "," feature ":" TIM: ENE "," mac ":" B0: BE: 76: 12: 4F: 01 "," mise à jour ": 0," led_off " : 0, "children": [{"id": "80067B24A755F99C4D6C1807455E09F91AB7B2AA00", "state": 0, "alias": "CezHeat", "on_time": 0, "next_action": {"type": - 1}} , {"id": "80067B24A755F99C4D6C1807455E09F91AB7B2AA01", "state": 1, "alias": "CezUVB", "on_time": 191207, "next_action": {"type": - 1}}, {"id": " 80067B24A755F99C4D6C1807455E09F91AB7B2AA02 "," state ": 1," alias ":" CyanHeat "," on_time ": 191207," next_action ": {" type ": - 1}}, {" id ":" 80067B24A7505F99C4D6C1 " 1, "alias": "ZanderHeat", "on_time": 191207, "next_action": {"type": - 1}}, {"id": "80067B24A755F99C4D6C1807455E09F91AB7B2AA04", "state": 1, "alias": " CairoHeat "," sur
Si quelqu'un pouvait me fournir une solution ou une explication sur les raisons pour lesquelles la sortie / le flux est coupé, ce serait très apprécié. J'ai utilisé une grande partie du code de ce module open source . Je cherche également à mieux comprendre comment tout cela fonctionne, donc si vous pouviez expliquer un peu plus, je l'apprécierais vraiment.
3 Réponses :
Selon la documentation , le bufsize L'argument spécifie uniquement la quantité maximale de données à lire:
socket.recv (bufsize [ flags]) Recevoir les données du socket. Le retour value est un objet bytes représentant les données reçues. Le maximum la quantité de données à recevoir à la fois est spécifiée par bufsize . Voir le Page de manuel Unix recv (2) pour la signification de l'argument optionnel drapeaux; il vaut zéro par défaut.
/ pre>Un autre exemple qui pourrait mieux convenir à votre application pourrait être d'attendre une taille de message fixe (1221 comme indiqué par votre question):
def recv_message(connection): data = list() transferred_bytes= 0 while transferred_bytes < 1221: data.append(connection.recv(min(1221-transferred_bytes, 2048))) if not data[-1]: raise RuntimeError("socket connection broken") transferred_bytes += len(data[-1]) return b''.join(data)
Comme indiqué par @SergeBallesta, il est préférable de rechercher le tramage dans le protocole de niveau supérieur (dans ce cas équilibré {et}) que de supposer que le message est de taille fixe.
Merci beaucoup! Ça marche; Cependant, il faut environ 30 secondes à chaque fois pour terminer la fonction entière (prend généralement une demi-seconde avec le code d'origine.) La prise intelligente elle-même est-elle responsable de la façon dont elle gère les connexions TCP?
Oui, le (premier) code attend que la prise «raccroche» avant de renvoyer les données. Si la prise ne raccroche pas, elle attendra plus de données.
@MarcoC Certaines recherches sur le protocole utilisé par votre plug semblent suggérer que les 4 premiers octets ( data [: 4]
) qui sont jetés contiennent en fait la longueur du message (moins les 4 octets pour la longueur ) donc ´struct.unpack ("> I", data [: 4]) [0] + 4´ doit être la longueur qui peut être utilisée avec le code 2
Ceci n'est qu'un complément à la réponse de SimonF. La cause du problème est en effet que TCP est un protocole de flux, donc les paquets peuvent être fragmentés ou réassemblés à n'importe quel état: pile TCP / IP de l'expéditeur, équipements réseau, pile TCP / IP du récepteur - j'inclus la bibliothèque de la couche utilisateur dans le Pile TCP / IP ici pour simplifier.
C'est la raison pour laquelle, vous devriez toujours utiliser un protocole de niveau supérieur au-dessus de TCP pour pouvoir diviser le flux en messages sensibles . Ici, vous pouvez noter que la fin d'un message est '}}}'
, vous pouvez donc concaténer l'entrée dans un tampon jusqu'à ce que vous trouviez ce modèle:
def recv_until(c, guard): """Receive data from a socket until guard if found on input""" guard_sz = len(guard) - 1 data = b'' sz = 0 while True: buffer = c.recv(1024) # read by chuncks of size 1024 (change value to your needs) got = len(buffer) data += buffer # concatenate in buffer ix = data.find(guard, sz - guard_sz if sz > guard_sz else 0) # is guard found? if ix != -1: return (data[:ix + guard_sz + 1], # return the message, and what could be behind it data[ix + guard_sz + 1:]) sz += got
Mieux vaut compter {et} au fur et à mesure, et rechercher le final} qui correspond à l'ouverture d'équilibrage {(par opposition à supposer que l'emboîtement est toujours 3 profond et à supposer qu'il n'y a pas d'espace blanc entre les} finales) : l'analyse selon laquelle cela est causé par le fait que TCP est un protocole de flux est correcte; une lecture TCP n'est pas garantie de remplir le tampon et vous devez continuer à lire jusqu'à ce que vous ayez toutes les données dont vous avez besoin, comme déterminé par le cadrage à un niveau de protocole plus élevé)
Marco, veuillez utiliser la méthode recv_into (buffer [ nbytes [ flags]])
pour le socket.
Mon exemple pour TCP-microserver:
import socket import struct def readReliably(s,n): buf = bytearray(n) view = memoryview(buf) sz = 0 while sz < n: k = s.recv_into(view[sz:],n-sz) sz += k # print 'readReliably()',sz return sz,buf def writeReliably(s,buf,n): sz = 0 while sz < n: k = s.send(buf[sz:],n-sz) sz += k # obj = s.makefile(mode='w') # obj.flush() # print 'writeReliably()',sz return sz # Client host = "127.0.0.1" port = 23456 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(10) s.connect((host,port)) # Request buf = struct.pack("4B",*[0x01,0x02,0x03,0x04]) io.writeReliably(s,buf,4) # Response sz,buf = io.readReliably(s,4) a = struct.unpack("4B",buf) print repr(a) # Server s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) #s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) s.bind((host,port)) s.listen(10) # unaccepted connections while True: sk,skfrom = s.accept() sz,buf = io.readReliably(sk,4) a = struct.unpack("4B",buf) print repr(a) # ... io.writeReliably(sk,struct.pack("4B",*[0x01,0x02,0x03,0x04]))
NameError: le nom 'io' n'est pas défini