【问题标题】:Download several files from a local server to a client从本地服务器下载多个文件到客户端
【发布时间】:2018-06-21 20:24:26
【问题描述】:

以下代码让我从服务器下载三个文件,分别称为 tmp.bsptmp.seqtmp.dms。但是,只有第一个文件 tmp.dms 被完全下载。另一个tmp.seqtmp.bsp的信息填满,tmp.bsp保持0KB。

客户:

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是下载一个文件,以前可以用,但是我要三个文件,我就评论了。

服务器:

import socket

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

while True:
    Content,Address = skServer.accept()
    print Address
    files = "C:\Users\Name_user\Desktop\Networking\Send_Receive/"
    fUploadFile = open(files+str('tmp.dms'),"rb")
    sRead = fUploadFile.read(1024)

    fUploadFile2 = open(files+str('tmp.seq'),"rb")
    sRead2 = fUploadFile2.read(1024)

    fUploadFile3 = open(files+str('tmp.bsp'),"rb")
    sRead3 = fUploadFile3.read(1024)

    while sRead:
        Content.send(sRead)
        sRead = fUploadFile.read(1024)

        Content.send(sRead2)
        sRead2 = fUploadFile2.read(1024)

#       Content.send(sRead3)   
#       sRead3 = fUploadFile3.read(1024)

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


skServer.close()

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

【问题讨论】:

    标签: python python-2.7 networking


    【解决方案1】:

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

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

    另一种可能的解决方案是计算文件大小并将该信息发送到文件数据的前面。这样,cilent 将知道每个文件需要多少数据。

    使用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)
    

    然后客户端可以通过读取四个字节和unpacking 到 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()
    

    【讨论】:

    • 感谢您解决了我的问题,我会注意缓冲区的大小,因为我确实必须移动大文件。
    • 很高兴为您提供帮助!如果您的文件大于 few kb,您可以使用循环或使用socket.setsockoptsocket.SO_SNDBUFsocket.SO_RCVBUF 设置为更高的值。如果文件太大而无法存储在内存中,则必须使用os.path.getsize 来获取文件大小。
    • 是的,文件大约有几个 GB
    • 我已更新代码以处理更大的文件。虽然我还没有用非常大的文件对其进行测试,但它应该可以正常工作。
    • 我会尽快测试并通知您
    【解决方案2】:

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

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

    上面的官方文档说:

    当一个recv返回0字节时,表示对方已经关闭(或正在关闭)连接。您将不会在此连接上收到更多数据。曾经。您或许可以成功发送数据

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

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

    阅读本文,

    现在有两组动词用于交流。您可以使用 send 和 recv,也可以将客户端套接字转换为类似文件的野兽并使用 read 和 write。后者是 Java 呈现其套接字的方式。我不打算在这里谈论它,只是警告你需要在套接字上使用flush。这些是缓冲的“文件”,一个常见的错误是写一些东西,然后读取以获取回复。如果没有刷新,您可能会永远等待回复,因为请求可能仍在您的输出缓冲区中。

    但如果您打算重复使用您的套接字进行进一步传输,您需要意识到套接字上没有没有 EOT。我再说一遍:如果一个套接字 send 或 recv 在处理 0 个字节后返回,则连接已断开。如果连接没有断开,你可能会永远等待一个 recv,因为套接字不会告诉你没有什么要读的了(现在)。现在,如果您稍微考虑一下,您就会意识到套接字的一个基本事实:消息必须是固定长度的(yuck),或者是定界的(耸肩),或者表明它们有多长(更好),或者以关闭连接结束。选择完全由您自己决定,(但有些方法比其他方法更正确)。

    希望这会有所帮助。

    【讨论】:

    • 我猜到了,并试图关闭连接,我什至认为这是因为我没有正确打开我的文件,但它无论如何都不起作用。多亏了你,现在我知道可能是什么问题了。你知道我怎么不能刷新缓冲区吗?
    • 即使使用了flush()你也不能刷新缓冲区?我的意思是清除缓冲区!
    • 不,它在任何方面都不起作用:/我无法使用刷新刷新我的缓冲区
    【解决方案3】:

    我并不完全精通 Python,但我认为您的 while 语句应该是这样的:

    while: sData or sData2 or sData3
    

    我可能有语法错误,但目前看起来你会在“sData”完成时停止并在那时停止下载 sData2 和 aData3,即使它们还没有完成。

    嗯——要么那个,要么“While”根本没有循环,它只是被用作“if”?不知道 API 很难分辨。

    【讨论】:

    • 我知道你的意思,这是我稍后会解决的问题。我首先需要做的是让我的三个文件最后都填满最初的信息,我不知道为什么我把文件 2 和 3 弄混了
    • 您是否打算按照与客户端文件相反的顺序列出服务器文件?您发送 bmp-seq-dms 但以相反的顺序读取它们。
    • 不,这是一个编辑错误,但它并没有改变任何东西。我还是卡住了,写完后我试图关闭文件,它也没有用
    猜你喜欢
    • 2011-10-22
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多