使用 TCP 套接字传输图像时缺少一像素行

Jan*_*nka 5 python tcp

我现在面临奇怪的错误,我有 python 脚本,它使用 TCP 套接字发送/接收数据,一切正常,但是当我尝试使用此脚本下载图像时,它会下载它,但是有一个缺少一个像素的行。关于如何解决它的任何想法?

服务器下载脚本:

    def download(self, cmd):
        try:
            self.c.send(str.encode(cmd))
            command,filename=cmd.split(' ')
            nFile = open(filename, 'wb')
            i = self.c.recv(1024)
            while not ('complete' in str(i)):   
                nFile.write(i)
                i = self.c.recv(1024)
            nFile.close()
            self.reset = True
            print('\nGot that file')
        except Exception as e:
            print(e)
Run Code Online (Sandbox Code Playgroud)

客户端上传脚本:

   def upload(self, filename):
    try:
        fd = open(filename, 'rb')
        data = fd.read(1024)
        while (data):
            self.s.sendall(data)
            data = fd.read(1024)
        self.s.send(str.encode('complete'))
        fd.close()
    except Exception as e:
        print(e)
Run Code Online (Sandbox Code Playgroud)

示例 - 您可以看到,缺少最后一行像素: 在此处输入图片说明

解决方案(1): 这不是解决方案,只是解决方法,使用另一个!

如果在将最后一块数据写入 nFile 之前删除有效负载的完整部分会发生什么?– mtrw

问题在于向服务器发送“完整”字符串,因为脚本没有足够的时间从图像中获取所有字节。因此,解决此问题的一种方法是将sleep(0.2)添加到脚本中。

客户端上传脚本:

   def upload(self, filename):
try:
    fd = open(filename, 'rb')
    data = fd.read(1024)
    while (data):
        self.s.sendall(data)
        data = fd.read(1024)
    sleep(0.2)
    self.s.send(str.encode('complete'))
    fd.close()
except Exception as e:
    print(e)
Run Code Online (Sandbox Code Playgroud)

解决方案(2):

TCP 是一种没有消息边界的流协议。这意味着在一个 recv 调用中可以接收多个发送,或者在多个 recv 调用中可以接收一个发送。

延迟解决方法可能无法可靠地工作。您需要在流中分隔消息。

——马克西姆·叶戈鲁什金

服务器下载脚本:

try:
    msg_header = self.c.recv(4)
    while len(msg_header) != 4:
        msg_header += self.c.recv(4 - len(msg_header))
    file_len = struct.unpack('<I', msg_header)[0]
    nFile = open(filename, 'wb')
    data = self.c.recv(file_len)
    while len(data) != file_len:
        data += self.c.recv(file_len - len(data))
    nFile.write(data)
    nFile.close()
    print('\nGot that file')
except Exception as e:
    print(e)
Run Code Online (Sandbox Code Playgroud)

客户端上传脚本:

try:
    file_len = os.stat(filename).st_size
    msg_header = struct.pack('<I', file_len)
    self.s.sendall(msg_header)
    fd = open(filename, 'rb')
    data = fd.read(file_len)
    while (data):
        self.s.sendall(data)
        data = fd.read(file_len)
    fd.close()
except Exception as e:
    print(e)
Run Code Online (Sandbox Code Playgroud)

Max*_*kin 3

问题在于向服务器发送“完整”字符串,因为脚本没有足够的时间从图像中获取所有字节。因此解决此问题的一种方法是将 sleep(0.2) 添加到脚本中。

TCP 是一种没有消息边界的流协议。这意味着send一次recv调用可以接收多个 s,也send可以在多个调用中接收一个recv

延迟解决方法可能无法可靠地工作。您需要在流中分隔消息。

有两种常见的方式来分隔流中的消息:

  1. 为消息添加标头前缀。
  2. 以后缀结束消息。

由于您发送的是二进制数据,任何后缀自然可以出现在有效负载中。除非后缀比有效负载长,这是不切实际的。

因此,您可能需要在此处为​​您的有效负载添加固定大小的标头。在这种特殊情况下,具有 4 字节二进制文件长度的标头就足够了。例如:

file_len = os.stat(filename).st_size
msg_header = struct.pack('<I', file_len)
self.s.sendall(msg_header)
Run Code Online (Sandbox Code Playgroud)

接收方需要先读取头部:

msg_header = self.s.recv(4)
while len(msg_header) != 4:
    msg_header += self.s.recv(4 - len(msg_header))
file_len = struct.unpack('<I', msg_header)
Run Code Online (Sandbox Code Playgroud)

然后file_len从套接字中准确读取。

了解正在接收的文件的大小还允许您预分配缓冲区以避免内存重新分配和/或预分配整个文件以最大限度地减少磁盘碎片或避免文件传输开始后出现磁盘空间不足错误。