【发布时间】:2020-04-03 18:55:58
【问题描述】:
我现在遇到一个奇怪的错误,我有 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)
客户端上传脚本:
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)
解决方案(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)
解决方案(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)
客户端上传脚本:
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)
【问题讨论】:
-
如果在将最后一个数据块写入
nFile之前删除负载的complete部分会发生什么? -
哇...这么简单的解决方法,我现在感觉很愚蠢。但是,我仍然不明白为什么实际上将“创建”字解析到图像文件中,当它必须检查接收到的字节是否是“下载”时,如果它必须停止写入会话。
-
我猜额外的字节会混淆图像解析器。图像标头可能包含有关预期字节数的一些信息,并且大于指定标头的文件会导致奇怪的行为。图像显示可能已经崩溃,可能通过忽略额外的字节显示正确的东西,真的不可能说会发生什么
-
不需要在
int(file_len[0])中转换为int,它已经解压了int。您还应该检查send的返回值,因为不能保证发送整个缓冲区。换句话说,用循环实现sendall函数。 -
@JanVodenka 我是说
int(...)位是不必要的。只是file_len = struct.unpack('<I', msg_header)[0]。虽然这是一件小事......