zoukankan      html  css  js  c++  java
  • 简单的网络文件传输示例,多线程传一个目录!(pickle很实用)

    简单的网络文件传输示例,多线程传一个目录!(pickle很实用) « Xiaoxia[PG]

    简单的网络文件传输示例,多线程传一个目录!(pickle很实用)

    写了很多年C/C++代码了,有时候换了一种语言去写程序,还是发觉自己的代码始终没有摆脱C的风格和思想。

    正当我考虑是否用Python里struct的pack和unpack函数来实现C里的结构功能的时候,我想起来有个东西叫做pickle,可以把一个对象编译成字符串然后保存到外部文件。既然可以这样做,那么我想可不可以用它来把一些数据都用pickle来打包,然后把生成的字符串通过网络传输到另外一个程序,然后得到这些数据内容呢?经过了一些测试,我发现是可行的。

    在Python的官网文档上说,有个叫cPickle的东西,和pickle实现了一样的功能,只不过cPickle是用C语言实现的,工作起来效率高很多。另外还了解到Python的marshal也可以实现一样的功能,只不过它没有文档化,不是一个公共的库,会随着版本变化而改变,所以不推荐使用。

    对pickle使用方法的详细文档在http://docs.python.org/library/pickle.html


    用pickle把一个复杂对象'all'保存到文件,然后重新加载到内存的'all2'

    >>> import pickle
    >>> list_ = [1, 2, 3, 4, 5]
    >>> dict_ = {'a':1, 'b':2, 'c':3}
    >>> all = [list_, dict_]
    >>> pickle.dump(all, file("temp", "wb"))
    >>> all2 = pickle.load(file("temp", "rb"))
    >>> print all
    [[1, 2, 3, 4, 5], {'a': 1, 'c': 3, 'b': 2}]
    >>> print all2
    [[1, 2, 3, 4, 5], {'a': 1, 'c': 3, 'b': 2}]

    下面写一个例子,实现一个目录传输功能,即能够把一个目录下的所有文件(包含子目录)传送到网络的另一端。通常可以作为备份或者转移文件来用。该例子包含两个部分,一个是服务器部分,监听一个端口2011,等待客户端发起文件传输的请求。另一个是客户端部分,连接服务器传输用户指定的目录文件。

    服务器server.py的代码。每当有一个连接请求,就产生一个新的线程处理文件接收工作。

    1. # -*- coding: utf8 -*-  
    2. import socket, cPickle, os, threading, struct  
    3.   
    4. def receive_process(clientfd):  
    5.     clientReader = clientfd.makefile("rb")  
    6.     while True:  
    7.         # 接收数据包的大小  
    8.         data = clientReader.read(4)  
    9.         if len(data)!=4break  
    10.         dataLength = struct.unpack("I", data)[0]  
    11.         data = clientReader.read(dataLength)  
    12.         packet = cPickle.loads(data)  
    13.         path = packet["path"]  
    14.         # 递归创建目录  
    15.         parent = os.path.dirname(path)  
    16.         if not os.path.exists(parent):  
    17.             os.makedirs(parent)  
    18.         file(path, "wb").write(packet["data"])  
    19.         print "Received file", path  
    20.         clientfd.send('\xff')  
    21.   
    22. def server_process():  
    23.     fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
    24.     # 设置重用标记,这样重启程序的时候不会提示端口被占用。  
    25.     fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
    26.     fd.bind(("", 2011))  
    27.     fd.listen(5)  
    28.     while True:  
    29.         # 等待客户端连接  
    30.         clientfd, addr = fd.accept()  
    31.         thread = threading.Thread(target = receive_process, args = (clientfd, ))  
    32.         # 设置Daemon属性可以让server结束,则所有子线程必须也退出  
    33.         thread.setDaemon(True)  
    34.         thread.start()  
    35.           
    36.   
    37. if __name__ == '__main__':  
    38.     try:  
    39.         server_process()  
    40.     except KeyboardInterrupt:  
    41.         exit()  

    客户端程序client.py的代码。首先遍历一遍要传输的目录文件,添加到队列中,然后启动5个线程,获取队列中的路径进行文件传输。

    1. # -*- coding: utf8 -*-  
    2. import socket, os, struct, threading, sys, cPickle  
    3. import Queue  
    4. # 使用5个线程发送  
    5. ThreadCount = 5  
    6. sendQueue = Queue.Queue()  
    7. remoteHost = ""  
    8. localPath = ""  
    9. targetPath = ""  
    10.   
    11. def searchPath():  
    12.     fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
    13.     fd.connect((remoteHost, 2011))  
    14.     while True:  
    15.         try:   
    16.             path = sendQueue.get_nowait()  
    17.         except:   
    18.             print "Thread", threading.currentThread().name, "exited"  
    19.             return  
    20.         print threading.currentThread().name, ":""Sending", path  
    21.         target = os.path.join(targetPath, path[len(localPath):].lstrip("/"))  
    22.         packet = {"path" : target, "data" : file(path, "rb").read() }  
    23.         data = cPickle.dumps(packet)  
    24.         fd.send(struct.pack("I", len(data)))  
    25.         fd.send(data)  
    26.         replyCode = fd.recv(1)  
    27.         if replyCode[0] != '\xff':  
    28.             print "Failed to send file", path  
    29.   
    30. if __name__ == "__main__":  
    31.     if len(sys.argv)!=4:  
    32.         print "Usage: senddir SourcePath TargetPath IP"  
    33.         exit()  
    34.     localPath, targetPath, remoteHost = sys.argv[1:]  
    35.     if os.path.exists(localPath):  
    36.         # 枚举目录文件,包含子目录  
    37.         for parent,dirs,files in os.walk(localPath):  
    38.             for f in files: #把每个文件放入发送队列  
    39.                 sendQueue.put(os.path.join(parent, f))  
    40.         print "Found", sendQueue.qsize(), "files!"  
    41.         for i in range(ThreadCount):  
    42.             threading.Thread(target = searchPath).start()  
    43.     else:  
    44.         print "File not found:", path  

    测试效果

    发送方效果图:

    接收方效果图:

  • 相关阅读:
    ProviderManager
    C#.NET常见问题(FAQ)-如何把定义存放类实例的数组
    C#.NET常见问题(FAQ)-命名空间namespace如何理解
    C#.NET常见问题(FAQ)-索引器indexer有什么用
    C#.NET常见问题(FAQ)-构造器constructor有什么用
    C#.NET常见问题(FAQ)-public private protectd internal有什么区别
    C#.NET常见问题(FAQ)-override覆盖和virtual虚类如何理解
    C#.NET常见问题(FAQ)-如何使用右下角托盘图标notifyIcon
    C#.NET常见问题(FAQ)-如何使用变量访问控件属性
    C#.NET常见问题(FAQ)-如何使用变量动态添加控件
  • 原文地址:https://www.cnblogs.com/lexus/p/2843333.html
Copyright © 2011-2022 走看看