我目前正在尝试使用python脚本控制智能配电盘。为此,我在套接字模块上使用了TCP连接。大约有75%的时间,我得到了所需的响应/数据,并且一切运行正常。但是,大约有25%的时间,响应被截断为完全相同的长度,即1024个字节。这对我来说没有任何意义,因为我的缓冲区大小实际上设置为2048个字节。我在使用recv()之间等待的速度似乎也没有影响/导致这一情况。 TCP完全是字节流,这是否仍可能与数据包分段有关?
主要代码
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
加密功能
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
解密功能
def decrypt(string):
key = 171
result = ""
for i in string:
a = key ^ ord(i)
key = ord(i)
result += chr(a)
return result
我收到的字符串本身。它最像是无关紧要的,但我想无论如何我都会把它包括在内。这是变量rec的值。
所需的常规输出
所需的全部输出
{“系统”:{“ get_sysinfo”:{“ sw_ver”:“ 1.0.6内部版本180627 Rel.081000“,” hw_ver“:” 1.0“,”型号“:” HS300(US)“,” deviceId“:” 80067B24A755F99C4D6C1807455E09F91AB7B2AA“,” oemId“:” 5C9E6254BEBAED63B2B6102966D24C17“,”“ hwEAsA4678” 8“ “:-60,” longitude_i“:-1222955,” latitude_i“:379078,” alias“:” TP-LINK_Power Strip_4F01“,” mic_type“:” IOT.SMARTPLUGSWITCH“,”功能“:” TIM:ENE“,” mac“:” B0:BE:76:12:4F:01“,”正在更新“:0,” led_off“ :0,“ children”:[{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA00”,“ state”:0,“ alias”:“ CezHeat”,“ on_time”:0,“ next_action”:{“ type”:-1}} ,{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA01”,“状态”:1,“别名”:“ CezUVB”,“ on_time”:191208,“ next_action”:{“ type”:-1}},{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA02“,”状态“:1,”别名“:” CyanHeat“,”开启时间“:191208,”下一个动作“:{”类型“:-1}},{” id“:” 80067B24A755F99C4D6C1807455E09A 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}}}
异常和稀有输出
切断输出
{“系统”:{“ get_sysinfo”:{“ sw_ver”:“ 1.0.6内部版本180627 Rel.081000“,” hw_ver“:” 1.0“,”型号“:” HS300(US)“,” deviceId“:” 80067B24A755F99C4D6C1807455E09F91AB7B2AA“,” oemId“:” 5C9E6254BEBAED63B2B6102966D24C17“,”“ hwEAsA4678” 8“ “:-59,” longitude_i“:-1222955,” latitude_i“:379078,” alias“:” TP-LINK_Power Strip_4F01“,” mic_type“:” IOT.SMARTPLUGSWITCH“,”功能“:” TIM:ENE“,” mac“:” B0:BE:76:12:4F:01“,”正在更新“:0,” led_off“ :0,“ children”:[{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA00”,“ state”:0,“ alias”:“ CezHeat”,“ on_time”:0,“ next_action”:{“ type”:-1}} ,{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA01”,“状态”:1,“别名”:“ CezUVB”,“ on_time”:191207,“ next_action”:{“ type”:-1}},{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA02“,”状态“:1,”别名“:” CyanHeat“,” on_time“:191207,” next_action“:{” type“:-1}},{” id“:” 80067B24A755F99C4D6C1807455E09A 1,“ alias”:“ ZanderHeat”,“ on_time”:191207,“ next_action”:{“ type”:-1}},{“ id”:“ 80067B24A755F99C4D6C1807455E09F91AB7B2AA04”,“ state”:1,“ alias”:“ CairoHeat”,“上
如果有人可以为我提供解决方案或解释,说明为什么输出/流被切断,将不胜感激。我使用了来自该开源module的许多代码。我还希望更多地了解这一切的工作原理,因此,如果您能多解释一点,我将非常感谢。
答案 0 :(得分:3)
根据documentation,bufsize参数仅指定要读取的最大数据量:
socket.recv(bufsize [,标志]) 从套接字接收数据。回报 value是一个字节对象,代表接收到的数据。 最大 bufsize 指定一次接收的数据量。见 Unix手册页recv(2)中可选参数的含义 标志;默认为零。
为确保完整的数据传输,可以使用类似这样的函数,该函数等待套接字连接结束(由recv返回,并返回空字符串):
def recv_all(connection):
"""
Function for all data
:param connection: socket connection
:return: received data
"""
data = list()
while True:
data.append(connection.recv(2048))
if not data[-1]:
return b''.join(data)
另一个可能更适合您的应用程序的示例可能是等待固定的消息大小(问题所指示的值为1221):
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)
答案 1 :(得分:1)
这只是对SimonF答案的补充。问题的原因确实是TCP是一种流协议,因此数据包可以在任何状态下进行分段或重组:发送方TCP / IP堆栈,网络设备,接收方TCP / IP堆栈-我将用户层库包含在TCP / IP堆栈在这里用于简化。
这就是为什么您应该始终使用TCP之上的更高级别协议以便能够将流拆分为 sensible 消息的原因。在这里,您可能会注意到消息的结尾是'}}}'
,因此可以将输入连接到缓冲区中,直到找到该模式为止:
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
诀窍是在可以将防护拆分为两个区块的情况下,从最后一个区块开始考虑guard_sz字节。
答案 2 :(得分:0)
Marco,请对套接字使用recv_into(buffer[, nbytes[, flags]])
方法。
我的TCP微型服务器示例:
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]))