Python套接字模块:Recv()数据响应中断

时间:2019-01-08 06:04:32

标签: python python-2.7 sockets networking tcp

说明

我目前正在尝试使用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的许多代码。我还希望更多地了解这一切的工作原理,因此,如果您能多解释一点,我将非常感谢。

3 个答案:

答案 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]))