将多个文件从本地服务器下载到客户端

时间:2018-06-21 20:24:26

标签: python python-2.7 networking

以下代码使我可以从服务器向客户端下载名为 tmp.bsp tmp.seq tmp.dms 的三个文件。但是,仅第一个文件 tmp.dms 已完全下载。另一个 tmp.seq 填充了 tmp.bsp tmp.bsp 保持0KB的信息。

客户端

numpy

import socket import socket skClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM) skClient.connect(("127.0.0.1",2525)) sData = "Temp" sData2 = "Temp" sData3 = "Temp" while True: sData = skClient.recv(1024) fDownloadFile = open("tmp.dms","wb") sData2 = skClient.recv(1024) fDownloadFile2 = open("tmp.seq","wb") sData3 = skClient.recv(1024) fDownloadFile3 = open("tmp.bsp","wb") while sData: fDownloadFile.write(sData) sData = skClient.recv(1024) fDownloadFile.close() fDownloadFile2.write(sData2) sData2 = skClient.recv(1024) fDownloadFile2.close() fDownloadFile3.write(sData3) sData3 = skClient.recv(1024) fDownloadFile3.close() print "Download over" break skClient.close() 是一个计数器,其打印用于调试。 n是要下载一个文件,并且可以正常工作,但是由于我要三个文件,所以我只对其发表了评论。

服务器:

sFileName

我正在使用的文件:
server2.py is my server
Execution

3 个答案:

答案 0 :(得分:2)

代码的主要问题是您要发送/接收任意数量的数据。如果您的缓冲区(1024)小于文件大小,则客户端的文件将包含较少的信息,如果它较大,则文件可能包含更多的信息(来自下一个文件的数据)。

您可以通过发送一个表示文件结尾的值来解决此问题。此方法的问题在于,该值不能包含在任何文件中,并且客户端必须在接收到的数据中扫描该值。

另一种可能的解决方案是计算文件大小,然后在文件数据之前发送该信息。这样,管理员将知道每个文件需要多少数据。

使用struct.pack,我们可以创建一个文件大小最小的四个字节的标头。

def send_file(soc, path):
    with open(path, 'rb') as f:
        data = f.read()
    size = struct.pack('!I', len(data))
    soc.send(size + data)

然后,客户端可以通过读取四个字节并将unpack设置为int来获取文件大小。

def recv_file(soc, path):
    size_header = soc.recv(4)
    size = struct.unpack('!I', size_header)[0]
    data = soc.recv(size)
    with open(path, 'wb') as f:
        f.write(data)

请注意,如果文件大小大于套接字缓冲区,则一次调用发送/接收文件可能会引起套接字错误。在这种情况下,您必须循环读取较小的数据块,或者使用socket.setsockopt增加缓冲区大小。

这是上述功能的修改版本,可以处理大文件:

import struct
import os.path

def send_file(soc, path):
    file_size = os.path.getsize(path)
    size_header = struct.pack('!Q', file_size)
    soc.send(size_header)
    with open(path, 'rb') as f:
        while True:
            data = f.read(1024)
            if not data:
                break
            soc.send(data)

def recv_file(soc, path):
    size_header = soc.recv(8)
    file_size = struct.unpack('!Q', size_header)[0]
    chunks = [1024 for i in range(file_size / 1024)]
    with open(path, 'wb') as f:
        for chunk in chunks:
            f.write(soc.recv(chunk))
        f.write(soc.recv(file_size % 1024))

我还没有彻底测试过此代码,但是它适用于任何大小的文件。


在服务器中使用send_file函数的示例:

host = ''
skServer = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
skServer.bind((host,2525))
skServer.listen(10)
print "Server currently active"

Content,Address = skServer.accept()
print Address

files = ['tmp.bsp', 'tmp.seq', 'tmp.dms']
for file in files:
    send_file(Content, file)

Content.close()
print "Sending is over"
skServer.close()

在客户端中使用recv_file

skClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
skClient.connect(("127.0.0.1",2525))

files = ['tmp.bsp', 'tmp.seq', 'tmp.dms']
for file in files:
    recv_file(skClient, file)

print "Download over"
skClient.close()

答案 1 :(得分:1)

是的,是的,我确实运行了您的程序并发现了完全相同的问题。我没有足够的时间在这个问题上做更多的工作,但是我发现一些关键点可能会导致您找到正确的解决方法。

https://docs.python.org/2/howto/sockets.html

上面的官方文件说:

  

recv返回0字节时,表示另一端已关闭(或正在关闭)连接。您将不会再通过此连接收到任何数据。曾经您也许可以成功发送数据

这是第三个文件返回0字节时发生的情况。

但是为什么合并第二个和第三个文件,我想是因为套接字只是缓冲文件,我们可能需要在发送另一个文件之前尝试确保缓冲是清除的。

阅读此内容

  

现在有两组动词可用于交流。您可以使用send和recv,也可以将客户端套接字转换为类似文件的野兽并使用读写。后者是Java呈现其套接字的方式。除了警告您需要在套接字上使用冲洗功能之外,我这里不再讨论。这些是缓冲的“文件”,一个常见的错误是写一些东西,然后阅读以得到答复。如果没有刷新,您可能会永远等待答复,因为请求可能仍在输出缓冲区中。

     

但是,如果您打算重复使用套接字以进行进一步的传输,则需要意识到套接字上没有没有 EOT。我重复一遍:如果套接字发送或接收后处理0字节后返回,则说明连接已断开。如果连接没有断开,您可以永远等待一次接收,因为套接字不会告诉您(现在)没有更多内容可供阅读。现在,如果您考虑一下,您将认识到套接字的基本原理:消息必须是固定长度(讨厌)或定界(耸耸肩),或者指出它们有多长(更好),或者通过关闭连接来结束。选择完全是您的选择(但某些方法比其他方法正确)。

希望这会有所帮助。

答案 2 :(得分:0)

我并不完全精通Python,但我认为您的while语句应类似于:

while: sData or sData2 or sData3

我的语法可能不正确,但是目前看来,当“ sData”完成时,您将停止,并且即使它们尚未完成,也将在那时停止下载sData2和aData3。

嗯-那或“ While”根本没有循环,而只是用作“ if”?在不知道API的情况下很难说出来。