zoukankan      html  css  js  c++  java
  • 网络编程之文件传输实例

    一、文件传输包括两部分,服务端收发文件和客户端(即用户)收发文件。

      收发文件与远程执行命令的程序原理是一样的,比如客户端下载文件的过程:

        1、客户端提交下载命令;

        2、服务端接收命令,解析,执行下载文件的方法,即以读的方式打开文件,利用for循环读出一行行内容,

        然后发送(send)给客户端。

        3、客户端以写的方式打开文件,将接受的文件内容接收写入文件中。

     

    二、文件传输的几种模式

      1、基础版

        服务端文件夹应包括共享文件夹share和server.py

        server.py

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    import socket
    import struct
    import json
    import os
    share_dir = r'D:pycharmTest1Thrid_module网络编程文件传输文件传输基础版服务端share'
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server.bind(('127.0.0.1',3361))
    server.listen(5)
    
    while True:
        conn,client_addres = server.accept()
        while True:
            try:
                cmd = conn.recv(1024)
                if not cmd:break # 适用于linux
                # 执行客户端的命令,并返回执行后的信息
                # 1、解析客户端的指令,提取文件名
                data = cmd.decode('gbk').split()
                file_name = data[1]
                # 1、制作报头
                header_dic = {
                    'file_name':file_name, # 客户端需要下载的文件名
                    'md5':'xxxxxxxx',
                    'total_size':os.path.getsize('%s\%s'%(share_dir,file_name)) # 服务端对应文件的大小
                }
                # 2、编辑并发送报头长度
                header_json = json.dumps(header_dic) # 报头字典转字符串
                header_bytes = header_json.encode('gbk') # 字符串转bytes
                conn.send(struct.pack('i',len(header_bytes)))# 发送报头长度
                # 3、发送报头信息
                conn.send(header_bytes)
                # 4、发送真实数据
                with open('%s\%s'%(share_dir,file_name),'rb') as f:
                    for line in f:
                        conn.send(line)
            except ConnectionResetError:
                break
        conn.close()
    server.close()
    View Code

        客户端文件夹应包括下载或上传文件夹down和client.py

        client.py

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    import socket
    import struct
    import json
    downland_dir = r'D:pycharmTest1Thrid_module网络编程文件传输文件传输基础版客户端dowland'
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    client.connect(('127.0.0.1',3361))
    
    while True:
        cmd = input('输入下载的文件及后缀名>>:').strip()
        if not cmd:continue
        client.send(cmd.encode('gbk'))
        # 等待接收
        # 1、获取报头长度
        obj = client.recv(4)
        header_size = struct.unpack('i',obj)[0]
        # 2、获取报头内容
        header_bytes = client.recv(header_size)
        # 3、解析报头,获取真实数据长度
        header_data = header_bytes.decode('gbk') # bytes解码成字符串
        header_dic = json.loads(header_data)  # 字符串反序列化为字典(报头)
        total_size = header_dic['total_size'] # 得到真实数据长度
        file_name = header_dic['file_name']
        # 4、接收真实数据
        with open('%s\%s'%(downland_dir,file_name),'wb') as f:
            recv_size = 0
            while recv_size < total_size:
                line = client.recv(1024)
                f.write(line)
                recv_size += len(line)
                print('文件总大小:%s	已下载大小:%s'%(total_size,recv_size))
    
    client.close()
    View Code

      

      2、函数版

        服务端文件夹包括recieve、share文件夹和server.py

        server.py

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    import socket
    import struct
    import json
    import os
    share_dir = r'D:pycharmTest1Thrid_module网络编程文件传输文件传输优化_函数版服务端share'
    recieve_dir = r'D:pycharmTest1Thrid_module网络编程文件传输文件传输优化_函数版服务端
    ecieve'
    
    def get(cmd,conn):
        # 执行客户端的命令,并返回执行后的信息
        # 1、解析客户端的指令,提取文件名
        data = cmd.decode('gbk').split()
        file_name = data[1]
        # 1、制作报头
        header_dic = {
            'file_name': file_name,  # 客户端需要下载的文件名
            'md5': 'xxxxxxxx',
            'total_size': os.path.getsize('%s\%s' % (share_dir, file_name))  # 服务端对应文件的大小
        }
        # 2、编辑并发送报头长度
        header_json = json.dumps(header_dic)  # 报头字典转字符串
        header_bytes = header_json.encode('gbk')  # 字符串转bytes
        conn.send(struct.pack('i', len(header_bytes)))  # 发送报头长度
        # 3、发送报头信息
        conn.send(header_bytes)
        # 4、发送真实数据
        with open('%s\%s' % (share_dir, file_name), 'rb') as f:
            for line in f:
                conn.send(line)
    def put(cmd,conn):
        '''
        获取用户上传的内容
        :param cmd:
        :param conn:
        :return:
        '''
        # 1、接收报头的大小
        obj = conn.recv(4)
        header_size = struct.unpack('i', obj)[0]
        # 2、接收报头的信息
        head_bytes = conn.recv(header_size)
        # 3、解析报头信息并获取真实数据的大小
        head_data = head_bytes.decode('gbk') # bytes-->str
        head_dic = json.loads(head_data)  # str-->dict
        file_name = head_dic['file_name']
        total_size = head_dic['total_size']
        # 4、接收真实数据
        with open('%s\%s'%(recieve_dir,file_name),'wb') as f:
            recv_size = 0
            while recv_size < total_size:
                line = conn.recv(1024)
                f.write(line)
                recv_size += len(line)
                print('文件总大小为:%s	已接收的文件大小为:%s'%(total_size,recv_size))
    
    def run():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('127.0.0.1', 8889))
        server.listen(5)
        while True:
            conn,client_addres = server.accept()
            while True:
                try:
                    cmd = conn.recv(1024)
                    data = cmd.decode('gbk').split()
                    if data[0] == 'get':
                        get(cmd,conn)
                    elif data[0] == 'put':
                        put(cmd,conn)
    
                except ConnectionResetError:
                    break
            conn.close()
        server.close()
    
    if __name__ == '__main__':
        run()
    View Code

        客户端文件夹包括dowland、share文件夹和client.py

        client.py

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    import socket
    import struct
    import json
    import os
    downland_dir = r'D:pycharmTest1Thrid_module网络编程文件传输文件传输优化_函数版客户端dowland'
    share_dir = r'D:pycharmTest1Thrid_module网络编程文件传输文件传输优化_函数版客户端share'
    
    def get(client,cmd):
        '''
        用户下载功能
        :param client:
        :param cmd:
        :return:
        '''
        # 等待接收
        # 1、获取报头长度
        obj = client.recv(4)
        header_size = struct.unpack('i',obj)[0]
        # 2、获取报头内容
        header_bytes = client.recv(header_size)
        # 3、解析报头,获取真实数据长度
        header_data = header_bytes.decode('gbk')  # bytes解码成字符串
        header_dic = json.loads(header_data)  # 字符串反序列化为字典(报头)
        total_size = header_dic['total_size']  # 得到真实数据长度
        file_name = header_dic['file_name']
        # 4、接收真实数据
        with open('%s\%s' % (downland_dir, file_name), 'wb') as f:
            recv_size = 0
            while recv_size < total_size:
                line = client.recv(1024)
                f.write(line)
                recv_size += len(line)
                print('文件总大小:%s	已下载大小:%s' % (total_size, recv_size))
    def put(client,cmd):
        '''
        用户上传功能
        :param cmd:
        :return:
        '''
        # 1、制作报头
        file_name = cmd.split()[1]
        file_dic = {
            'file_name':file_name,
            'md5':'xxxxxxxx',
            'total_size':os.path.getsize('%s\%s'%(share_dir,file_name))
        }
        # 2、编辑并发送报头长度
        header_json = json.dumps(file_dic)
        head_bytes = header_json.encode('gbk')
        client.send(struct.pack('i',len(head_bytes)))
        # 3、发送报头信息
        client.send(head_bytes)
        # 4、发送真实数据信息
        with open('%s\%s'%(share_dir,file_name),'rb') as f:
            for line in f:
                client.send(line)
                # print('文件总大小为:%s	已上传文件大小为:%s'%(file_dic['total_size'],))
    def run():
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect(('127.0.0.1', 8889))
        while True:
            cmd = input('输入下载(get)或上传(put)的文件及后缀名>>:').strip()
            if not cmd:continue
            client.send(cmd.encode('gbk'))
            res = cmd.split()[0]
            if res == 'get':
                print('你正在进行下载操作...')
                get(client,cmd)
            elif res == 'put':
                print('你正在进行上传操作...')
                put(client,cmd)
    
        client.close()
    if __name__ == '__main__':
        run()
    View Code

     

      3、面向对象版

        服务端包括server_db文件夹(用于存放文件)和server.py

        server.py

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    import socket
    import struct
    import json
    import os
    
    class My_server:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        allow_reuse_addres = False # 端口重用
        max_size = 8192  # 最大单次接收字节数目
        coding = 'utf-8' # 编码,windows系统,mac或linux使用utf-8
        listen_queue = 5  # 监听数
        #server_dir = 'D:pycharmTest1Thrid_module网络编程文件传输文件传输优化_面向对象服务端server_db' # 服务器文件目录
        server_dir = 'server_db'
        def __init__(self,server_addres,bind_and_active=True):
            '''由构造函数调用来绑定套接字'''
            self.server_addres = server_addres
            self.socket = socket.socket(self.address_family,self.socket_type)
            if bind_and_active:
                try:
                    self.server_bind()
                    self.server_active()
                except:
                    self.server_close()
                    raise
    
        def server_bind(self):
            '''服务器socket套接字绑定方法'''
            if self.allow_reuse_addres:
                self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 设置端口重用
            self.socket.bind(self.server_addres)
            self.server_addres = self.socket.getsockname()  # 获取本地端点的地址
    
        def server_active(self):
            '''由构造函数调用以激活服务器 '''
            self.socket.listen(self.listen_queue)
    
        def get_request(self):
            '''从套接字获取请求和客户端地址'''
            return self.socket.accept()
    
        def close_request(self,request):
            '''调用以清理单个请求'''
            request.close()
    
        def server_close(self):
            '''调用以清理服务器'''
            self.socket.close()
    
        def run(self):
            '''入口程序'''
            while True:
                self.conn,self.client_addres = self.get_request()
                print('from client:',self.client_addres)
                while True:
                    try:  # 异常处理
                        head_struct = self.conn.recv(4)#接收报头
                        #if not head_struct:break # linux适用
                        print(head_struct)
                        head_len = struct.unpack('i',head_struct)[0] # 获取报头字典长度
                        head_json = self.conn.recv(head_len).decode(self.coding) #接收字典并转成字符串
                        head_dic = json.loads(head_json) # 将字符串转为字典
                        print(head_dic)
                        cmd = head_dic['cmd']
                        if hasattr(self,cmd): # 反射,判断是否有字符串对应的方法
                            func = getattr(self,cmd)  # 获取字符串对应的方法
                            func(head_dic)  # 执行对应方法
                    except Exception:
                        break
    
        def get(self,dic):
            '''执行用户输入的下载命令'''
            print(dic)
            cmd = dic['cmd']
            file_name = dic['filename'] # 获取下载的文件名
            file_path = os.path.normpath(os.path.join(self.server_dir,file_name)) # 获取文件位置
            if not os.path.isfile(file_path):
                print('文件%s不存在,请核对后再输入!'%file_name)
            else:
                file_size = os.path.getsize(file_path) # 获取文件大小
            # 制作报头
            head_dic = {
                'cmd':cmd,
                'filename':file_name,
                'filesize':file_size,
                'md5':'xxxxxx'
            }
            head_str = json.dumps(head_dic) # 字典转为字符串
            head_bytes= head_str.encode(self.coding) # 字符串转为bytes
            head_struct = struct.pack('i',len(head_bytes)) # 打包成固定字节长度的串
            # 发送报头长度
            self.conn.send(head_struct)
            # 发送报头
            self.conn.send(head_bytes)
            # 发送正式数据
            send_size = 0
            with open(file_path,'rb') as f:
                while send_size<file_size:
                    for line in f:
                        self.conn.send(line)
                        send_size += len(line)
                        print('文件总大小:%s,已发送文件:%s'%(file_size,send_size/file_size))
    
        def put(self,dic):
            '''接收用户上传的文件'''
            file_path = os.path.normpath(os.path.join(self.server_dir,dic['filename'])) #设置存放文件的位置
            file_size= dic['filesize'] # 获取上传文件的大小
            recv_size = 0
            with open(file_path,'wb') as f:
                while recv_size<file_size:
                    recv_data = self.conn.recv(self.max_size)
                    f.write(recv_data)
                    recv_size+=len(recv_data)
                    print("文件总大小:%s,已接收文件大小:%s"(file_size,recv_size))
    
    myserver = My_server(('127.0.0.1',3301))
    if __name__ == '__main__':
        myserver.run()
    View Code

        客户端包括client_db文件夹(存放文件)和client.py

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    import socket
    import json
    import os
    import struct
    
    class My_client:
        addres_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        allow_reuse_addres = False # 端口重用
        max_size = 8192 # 单次最大接收或发出文件数据量
        coding = 'utf-8' # 文件或命令编码格式
        listen_queue = 5
        #client_dir = 'D:pycharmTest1Thrid_module网络编程文件传输文件传输优化_面向对象客户端client_db'
        client_dir = 'client_db'
        def __init__(self,server_addres,connect=True):
          '''由构造函数调用来绑定套接字'''
          self.server_addres = server_addres
          self.socket = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
          if connect:
              try:
                  self.client_connect() # 调用链接方法
              except:
                  self.client_connect()
                  raise
        def client_connect(self):
            '''链接方法'''
            self.socket.connect(self.server_addres)
        def client_close(self):
            '''关闭链接方法'''
            self.socket.close()
        def run(self):
            '''客户端程序入口'''
            while True:
                res = input('输入getput 文件名+后缀>>').strip()
                if not res:continue
                str =res.split()
                cmd = str[0] # 获取get或put命令
                file_name = str[1]
                if hasattr(self,cmd):
                    func = getattr(self,cmd)
                    func(str)
        def get(self,res):
            '''执行下载方法'''
            cmd = res[0] # 得到下载命令
            file_name = res[1] # 得到文件名
            # 制作报头
            head_dic ={
                'cmd':cmd,
                'filename':file_name,
                'md5':'xxxxx'
            }
            print(head_dic)
            head_str = json.dumps(head_dic) # 字典转字符串
            head_byte= head_str.encode(self.coding) # 字符串转byte
            head_stuct = struct.pack('i',len(head_byte)) # byte打包为指定字节长度的包
            # 发送报头长度
            self.socket.send(head_stuct)
            # 发送报头里的数据
            self.socket.send(head_byte)
            # 接收下载信息的报头
            head_recv_struct = self.socket.recv(4)
            # 解析报头,获取下载文件的大小
            head_recv_size = struct.unpack('i',head_recv_struct)[0]
            # 接收下载信息的报头数据,并解码
            head_recv_data = self.socket.recv(head_recv_size).decode(self.coding)
            # 将解码的字符串反序列化为字典
            head_recv_dic = json.loads(head_recv_data)
            print(head_recv_dic)
            # 获取待接收的文件的大小和文件名
            file_size = head_recv_dic['filesize']
            recv_file_name = head_recv_dic['filename']
            # 接收下载数据
            file_path = os.path.normpath(os.path.join(self.client_dir, file_name))  # 设定下载文件存放位置
            recv_size = 0
            with open(file_path,'wb') as f:
                while recv_size < file_size:
                    recv_data = self.socket.recv(self.max_size)
                    f.write(recv_data)
                    recv_size += len(recv_data)
                    print('文件总大小:%s,已下载文件:%s'%(file_size,recv_size/file_size))
    
        def put(self, str):
            cmd = str[0]  # 获得'put'
            file_name = str[1]
            file_path = os.path.normpath(os.path.join(self.client_dir, file_name))
            # 制定报头
            if not os.path.isfile(file_path):
                # os.path.isfile(path)  #判断路径是否为文件
                print('%s is not exist' % file_name)
                return
            else:
                file_size = os.path.getsize(file_path)  # 获取文件大小
            head_dic = {
                'cmd': cmd,
                'filename': os.path.basename(file_path),
                'filesize': file_size,
            }  # 制作报头
            print(head_dic)
            head_json = json.dumps(head_dic)  # 将字典转为字符串
            head_bytes = bytes(head_json, encoding=self.coding)  # 字符串转为bytes
            print(head_bytes)  # b'{"cmd": "put", "filename": "pic.jpg", "filesize": 7}'
            head_struct = struct.pack('i', len(head_bytes))  # 打包成固定字节长度的bytes数据
            self.socket.send(head_struct)  # 发送报头长度
            self.socket.send(head_bytes)  # 发送报头
            send_size = 0
            with open(file_path, 'rb') as f:
                for line in f:
                    self.socket.send(line)
                    send_size += len(line)
                    print("你上传的文件总大小为:%d	已上传:%d%%" % (file_size, (send_size / file_size) * 100))
                print('上传成功')
    
    myclient = My_client(('127.0.0.1',3301))
    if __name__ == '__main__':
        myclient.run()
    View Code

     

      

  • 相关阅读:
    CSS3 动画-- 鼠标移上去,div 会旋转、放大、移动
    jquery 微信端 点击物理返回按钮,弹出提示框
    H5 canvas pc 端米字格 写字板
    【三剑客】awk函数
    【三剑客】awk运算符
    【三剑客】awk命令2
    【三剑客】awk命令
    磁盘
    用户管理
    定时任务Crond
  • 原文地址:https://www.cnblogs.com/schut/p/8680707.html
Copyright © 2011-2022 走看看