简单的网络文件传输示例,多线程传一个目录!(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()  
# -*- coding: utf8 -*-
import socket, cPickle, os, threading, struct

def receive_process(clientfd):
    clientReader = clientfd.makefile("rb")
    while True:
        # 接收数据包的大小
        data = clientReader.read(4)
        if len(data)!=4: break
        dataLength = struct.unpack("I", data)[0]
        data = clientReader.read(dataLength)
        packet = cPickle.loads(data)
        path = packet["path"]
        # 递归创建目录
        parent = os.path.dirname(path)
        if not os.path.exists(parent):
            os.makedirs(parent)
        file(path, "wb").write(packet["data"])
        print "Received file", path
        clientfd.send('\xff')

def server_process():
    fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置重用标记,这样重启程序的时候不会提示端口被占用。
    fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    fd.bind(("", 2011))
    fd.listen(5)
    while True:
        # 等待客户端连接
        clientfd, addr = fd.accept()
        thread = threading.Thread(target = receive_process, args = (clientfd, ))
        # 设置Daemon属性可以让server结束,则所有子线程必须也退出
        thread.setDaemon(True)
        thread.start()
        

if __name__ == '__main__':
    try:
        server_process()
    except KeyboardInterrupt:
        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  
# -*- coding: utf8 -*-
import socket, os, struct, threading, sys, cPickle
import Queue
# 使用5个线程发送
ThreadCount = 5
sendQueue = Queue.Queue()
remoteHost = ""
localPath = ""
targetPath = ""

def searchPath():
    fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    fd.connect((remoteHost, 2011))
    while True:
        try: 
            path = sendQueue.get_nowait()
        except: 
            print "Thread", threading.currentThread().name, "exited"
            return
        print threading.currentThread().name, ":", "Sending", path
        target = os.path.join(targetPath, path[len(localPath):].lstrip("/"))
        packet = {"path" : target, "data" : file(path, "rb").read() }
        data = cPickle.dumps(packet)
        fd.send(struct.pack("I", len(data)))
        fd.send(data)
        replyCode = fd.recv(1)
        if replyCode[0] != '\xff':
            print "Failed to send file", path

if __name__ == "__main__":
    if len(sys.argv)!=4:
        print "Usage: senddir SourcePath TargetPath IP"
        exit()
    localPath, targetPath, remoteHost = sys.argv[1:]
    if os.path.exists(localPath):
        # 枚举目录文件,包含子目录
        for parent,dirs,files in os.walk(localPath):
            for f in files: #把每个文件放入发送队列
                sendQueue.put(os.path.join(parent, f))
        print "Found", sendQueue.qsize(), "files!"
        for i in range(ThreadCount):
            threading.Thread(target = searchPath).start()
    else:
        print "File not found:", path

测试效果

发送方效果图:

接收方效果图:

posted on 2013-01-03 20:45  lexus 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lexus/archive/2013/01/03/2843333.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值