最近在做一个项目,涉及到本地机器和服务器之间的数据传输,因此也查了许多资料和博客。纵观来看,数据传输的一般步骤是先发送数据头,包括文件大小和文件名,然后开始传输数据。但是我的需求是发送端为Windows系统,而接收端为linux系统,使用之前的代码接收到的文件一直有损坏,导致后续工作失败。在查阅了无数资料后,终于找到一种解决办法,特记录在这里。
下面是部署在发送端的代码,和网上查找的代码基本相同:
import socket
import struct
import sys
import os
import time
if __name__ == '__main__':
file_name = sys.argv[1]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((socket.gethostname(), 12345))
# 定义文件头信息;
file_head = struct.pack('128sl', os.path.basename(file_name).encode(),
os.stat(file_name).st_size)
sock.send(file_head)
read_file = open(file_name, "rb")
while True:
# time.sleep(1)
file_data = read_file.read(10240)
if not file_data:
break
sock.send(file_data)
read_file.close()
sock.close()
下面是接收端的代码,这里主要对接收文件的地方进行了修改,代码如下:
import socket
import threading
import os
import struct
def sending_file(connection):
try:
file_info_size = struct.calcsize('128sl')
buf = connection.recv(file_info_size)
if buf:
file_name, file_size = struct.unpack('128sl', buf)
file_name = file_name.decode().strip('\00')
file_new_dir = os.path.join('receive')
# print(file_name, file_new_dir)
if not os.path.exists(file_new_dir):
os.makedirs(file_new_dir)
file_new_name = os.path.join(file_new_dir, file_name)
received_size = 0
w_file = open(file_new_name, 'wb')
print("start receiving file:", file_name)
while not received_size == file_size:
r_data = connection.recv(10240)
received_size += len(r_data)
w_file.write(r_data)
w_file.close()
print("接收完成!\n")
connection.close()
except Exception as e:
print(e)
if __name__ == '__main__':
host = socket.gethostname()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((host, 12345))
sock.listen(5)
print("服务已启动---------------")
while True:
connection, address = sock.accept()
print("接收地址:", address)
thread = threading.Thread(target=sending_file, args=(connection,))
thread.start()
这样修改的原因在于接收端并不是每次都能接收到buf_size大小的数据,因而在最后的一次connection.recv(file_size - received_size)不能保证一定能接收完数据,谨记。
另外,在不同的平台上可能会遇到这个问题:struct.error: unpack requires a buffer of 136 bytes。
这是由于平台的位数不同导致的,我的windows系统为32位,而linux系统为64位,因此应该将32位的机器上的128sl改为128sq,这样长度就相等了。如果想进一步拓展程序的兼容性,可以使用platform模块判断系统的位数和操作系统类别。