zoukankan      html  css  js  c++  java
  • 通信的几个程序

    1.大文件的上传与下载

    • 基于tcp协议的通信
    • 客户端只用传输信息,把信息装入字典,通过序列化将字典传给服务端,服务端实现功能;
    • 服务端每次接受数据都是向操作系统要,而不是一个recv对应一个send
    • 所以对于大文件可以按字节读,每次读取一个固定字节,通过os模块得到文件大小,文件大小累减到0,文件读完;
    #客户端client
    import socket
    import os
    import json
    
    sk = socket.socket()
    sk.connect(("127.0.0.1",8001))
    
    #让用户输入选项选择功能
    menu = {"1":"upload","2":"download"}
    for k,v in menu.items():
        print(k,v)
    num = input("请输入功能选项:")
    
    #上传功能  用户只管输入一些信息,存放在字典中,通过序列化发给服务端
    #功能都是由服务端实现
    if num == "1":
        dic = {"opt":menu.get(num),"filename":None,"filesize":None}
        file_path = input("请输入一个绝对路径:") # 文件的绝对路径
        filename = os.path.basename(file_path) # 文件名字
        filesize = os.path.getsize(file_path)  # 获取用户输入的路径中文件的大小
                                              #用于服务端接收的时候也要知道接收到多大才行
    
        dic["filename"] = filename
        dic["filesize"] = filesize
        str_dic = json.dumps(dic)
        sk.send(str_dic.encode("utf-8"))# 将被填充完成的字典先发送给服务器
    
        sk.recv(1024)# 为什么要有一个recv?
        #  因为上边send字典时,如果程序执行过快,可能会马上执行到下边的send(content)
        #  此时有可能会发生粘包,所以在此中间加一个recv,为了避免粘包
        with open(file_path,"rb") as f:
            while filesize:
                content = f.read(1024)
                sk.send(content)
                filesize -= len(content)
    
    elif num == "2":
        pass
    #服务端
    import socket
    import json
    sk = socket.socket()
    sk.bind(("127.0.0.1",8001))
    sk.listen()
    conn,addr = sk.accept()
    
    str_dic = conn.recv(100).decode("utf-8")
    #对于tcp读取多少字节都没有限制,而且对于单一的一个文件传输没有粘包现象
    因为在传输过程中,每一段数据都是有编号的,缓存区会自动帮你拆包组合成数据;
    
    conn.send(b'ok')#为什么需要一个send,防止与下面的recv发生粘包
    
    
    # str_dic = {"opt":menu.get(num),"filename":None,"filesize":None}
    dic = json.loads(str_dic)
    if dic["opt"] == "upload":
        filename = "1"+ dic["filename"] #避免文件名重复

    with open(filename,"ab") as f: while dic['filesize']: content = conn.recv(1024) f.write(content) dic['filesize'] -= len(content) elif dic["opt"] == "download": pass conn.close() sk.close()
    • 通过上面的代码可以看出,在客户端分别传输字典和数据时会容易发生粘包现象;
    • 解决方法,如果知道字典有多长,直接可以先按长度接收了字典,再接收数据,引入struct模块
    import struct
    
    a = {'a':'p','o':'a'}
    sa = len(a)#最大的长度是21亿
    
    s = struct.pack('i',sa)     #这里会将数据的长度打包成一个固定的值,一个四位点进制;
    print(s,len(s))        #这个会打包成一个bytes类型的数据
    
    
    print(struct.unpack('i',s)) #拆包的时候,得到是一个元组,第一位是你数据的长度;
    
    #输出结果
    b'x02x00x00x00' 4
    (2,)

    #第二版上传下载

    #服务器
    import
    socket import json import os import struct sk = socket.socket() sk.bind(('192.168.12.13',8090)) sk.listen() coon,addr = sk.accept() #通过struct模块将字典长度取出,再按长度接收字典 len_dic = coon.recv(4)#先将字典的长度取出 byte_dic = struct.unpack('i',len_dic)[0] str_dic = coon.recv(byte_dic).decode('utf-8') dic = json.loads(str_dic) print(dic) if dic['opt'] == 'uplaod': filename ='1' + dic['filename'] with open(filename,'ab')as f: while dic['filesize']: content = coon.recv(1024) f.write(content) dic['filesize'] -= len(content) elif dic['opt'] == 'download': dic1 = {'tuzhi.py':None,'120180817_095545.mp4':None} filese1 = os.path.getsize('tuzhi.py') filese2 = os.path.getsize('120180817_095545.mp4') dic1['tuzhi.py'] = filese1 dic1['120180817_095545.mp4'] = filese2 str_dic1 = json.dumps(dic1).encode('utf-8') coon.send(str_dic1) num = coon.recv(1024).decode('utf-8') ls = ['tuzhi.py','120180817_095545.mp4'] file1_size = os.path.getsize(ls[int(num)-1]) with open(ls[int(num)-1],'rb')as f1: while file1_size: s = f1.read(1024) coon.send(s) file1_size -= len(s) coon.close() sk.close()
    #客户端
    import socket
    import json
    import os
    import struct
    
    sk = socket.socket()
    sk.connect_ex(('192.168.12.13',8090))
    #创建一个字典用来实现功能
    menu = {'1':'uplaod','2':"download"}
    for k,v in menu.items():
        print(k,v)
    cc = input('输入序号选择>>>:')
    #上传大文件
    if cc == '1':
        #创建字典用来把用户的想要实现的功能装在字典中发给服务端
        dic = {'opt':menu.get(cc),'filename':None,'filesize':None}
        filename = input("输入文件路径>>>:")
        file_path = os.path.basename(filename)
        file_size = os.path.getsize(filename)
        #填充字典
        dic['filename'] = file_path
        dic['filesize'] = file_size
        #把字典作为字符串传输,通过struct模块避免粘包现象
        str_dic = json.dumps(dic)
        len_dic = len(str_dic)
        byte_dic = struct.pack('i',len_dic)
        sk.send(byte_dic+str_dic.encode('utf-8'))
        #按字节读取文件
        with open(filename,'rb')as f:
            while file_size:
                content = f.read(1024)        #这里可以少不可以多
                sk.send(content)
                file_size -= len(content)
    elif cc == '2':
        dic = {'opt': menu.get(cc), 'filename': None, 'filesize': None}
        str_dic = json.dumps(dic)
        len_dic = len(str_dic)
        byte_dic = struct.pack('i', len_dic)
        sk.send(byte_dic + str_dic.encode('utf-8'))
    
        dic_str = sk.recv(1024).decode('utf-8')
        dic1 = json.loads(dic_str)
        ls = []
        for k,v in enumerate(dic1.keys(),1):
            ls.append(v)
            print(k,v)
        num = input("请用序号选择文件>>>:")
        filesize = dic1[ls[int(num)-1]]
        file_name = '666'
        sk.send(num.encode('utf-8'))
        with open(file_name,'ab')as f1:
            while filesize:
                filestr = sk.recv(1024)
                f1.write(filestr)
                filesize -= len(filestr)
    else:
        print('输入错误!')
    sk.close()

    2.执行系统命令 

    1)在py中如何执行系统命令

    • 在工作中有时会让查看当前服务器的状态,服务器一般都是租用大公司的,所以通过一个模块就能了解到当前服务器的状态
    • 模块subprocess中的Popen方法,编码以当前系统为准,如果是windows,就用jbk解码编码,且只能从管道中读取一次结果;

    #subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)的一些参数:

    • cmd:代表系统命令
    • shell=True代表这条命令是系统命令,告诉操作系统将cmd当做系统命令去执行
    • subprocess.stdout:执行系统命令后返回的一条正确结果;
    • subprocess.stderr:执行系统命令后返回的一条错误的结果;
    #服务器
    sk = socket.socket() sk.bind(('127.0.0.1',8090)) sk.listen() coon,addr = sk.accept() while 1: mes = coon.recv(1024).decode('utf-8') result = subprocess.Popen(mes,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) #这两个结果总会有一个为空 byte_out = result.stdout.read() byte_err = result.stderr.read() if not byte_err: coon.send(byte_out) else: coon.send(byte_err) coon.close() sk.close()
    #客户端
    sk = socket.socket()
    sk.connect_ex(('127.0.0.1',8090))
    while 1:
        dir_out = input(">>>:")
        sk.send(dir_out.encode('utf-8'))
    
        #因为读取的是当前操作系统的内容,所以编码格式为gbk
        result = sk.recv(1023)
        print(result.decode('gbk'))
    
    sk.close()

    3.切换目录

    • 主要思想:将客户端输入的第一次绝对路径做为基础路径,在此上面做一些截取或则添加路径;
    • 问题是:在创建encode编码解码类时,tcp的客户端和服务器的对象不一样,所以要分开写方法
    • os模块中的listdir方法可以获取到当前路径下的所有文件和文件夹
    • 在客户端输入cd进入下一层时,要判断下一层是否是文件夹;
    #编码重复写编码和解码的过程
    import socket
    
    #创建一个类基于socket类
    class My_socket(socket.socket):
        def __init__(self, encond_ing='utf-8'):
            #先定义好一个默认值参数encond_ing,再调用父类方法
            self.encond_ing = encond_ing
            super().__init__()
        #服务器的连接对象方法
        def r_accept(self):
            coon, addr = self.accept()
            self.coon = coon
        #服务器通过链接对象接收和发送
        def s(self, msg):
            return self.coon.send(msg.encode(self.encond_ing))
        def r(self, num):
            str_msg = self.coon.recv(num).decode(self.encond_ing)
            return str_msg
        #服务器的关闭方法
        def cllo(self):
            return self.coon.close()
        #客户端的接收和发送方法
        def rc(self, num):
            str_msg = self.recv(num).decode(self.encond_ing)
            return str_msg
        def sc(self, msg):
            return self.send(msg.encode(self.encond_ing))
    #服务器
    from my_tcp import My_socket
    import os
    import json
    import struct
    
    sk = My_socket()
    sk.bind(('127.0.0.1',8090))
    sk.listen()
    sk.r_accept()    #等待连接,
    
    #接收到客户端第一次输入的绝对路径
    str_msg = sk.r(1024)
    
    def send_data(path):
        '''
        向客户端发送目录列表
        主要用到lisdir(绝对路径)下的所有目录
        '''
        lis_dir = os.listdir(path)
        #将列表序列化为字符串
        str_dir = json.dumps(lis_dir)
        sk.s(str_dir)
        return lis_dir
    def up_file(path):
        '''
        向客户端发送..上一层目录的情况
        切割客户端输入的绝对路径,因为在开始的时候加了一个/,所以列表中会有一个空字符
        然后往上一层的话直接就取切割掉最后一位的绝对路径,再拼接为字符串再加一个/
        '''
        split_path = path.split('\')[:-2]
        split_path = '/'.join(split_path)+'/'
        lst = send_data(split_path)
        return split_path,lst
    def down_file(path,path_list,num):
        '''
        通过客户端输入序号进入下一级目录
        :param path: str_dir
        :return: None
        '''
        down_path = path +path_list[int(num)-1]+'/'
        list_dir = send_data(down_path)
        return down_path,list_dir
    
    #在返回上一层目录中,如果C:不加/,则进入到当前文件的目录下,所以避免这种情况
    #则在开始的时候就加上/
    
    current_dir = str_msg + '/'
    ls = send_data(current_dir)
    while 1:
        #struct模块避免发生粘包
        len_menu = sk.r(4).encode('utf-8')
        len_menu = struct.unpack('i',len_menu)[0]
        menu = sk.r(len_menu)
        if menu == '..':
            current_dir,ls = up_file(current_dir)
        elif menu == 'cd':
            str_num = sk.r(1024)
            if os.path.isdir(current_dir + ls[int(str_num)-1]):
                current_dir, ls = down_file(current_dir,ls,str_num)
                continue
            else:
                s1 = json.dumps("这不是文件夹")
                sk.s(s1)
                continue
        else:
            sk.cllo()
            break
    
    
    sk.close()
    #客户端
    from my_tcp import My_socket
    import os
    import json
    import struct
    
    sk = My_socket()
    sk.connect_ex(('127.0.0.1',8090))
    
    #先让客户端输入一个基础路径
    #切换目录过程中都是以此为进行的
    abs_path = input(">>>:")
    abs_path = os.path.abspath(abs_path)
    sk.sc(abs_path)
    str_dir = sk.rc(1024)
    list_dir = json.loads(str_dir)
    
    for k,v in enumerate(list_dir,1):
                print(str(k)+ ':' + v,end=' ')
    print()
    while 1:
        menu = input("选择功能>>>:")
        #避免发生粘包
        len_menu = len(menu)
        slen = struct.pack('i',len_menu)
        sk.send(slen)
        sk.sc(menu)
        if menu.upper() == 'Q':
            break
        if menu == '..':
            str_dir = sk.rc(1024)
            list_dir = json.loads(str_dir)
            for k, v in enumerate(list_dir, 1):
                print(str(k)+ ':' + v, end=' ')
            print()
        elif menu == 'cd':
            num = input("按序号选择>>>:")
            sk.sc(num)
            str_dir = sk.rc(1024)
            list_dir = json.loads(str_dir)
            if type(list_dir) is str:
                print(list_dir)
                continue
            else:
                for k, v in enumerate(list_dir, 1):
                    print(str(k)+ ':' + v, end=' ')
                print()
        else:
            print("输入错误!")
            continue
    
    sk.close()

     

  • 相关阅读:
    Web前端的状态管理(State Management)
    使用iScroll实现上拉或者下拉刷新
    实现checkbox组件化(Component)
    HTML5 文件异步上传 — h5uploader.js
    利用javascript和WebGL绘制地球 【翻译】
    利用JS跨域做一个简单的页面访问统计系统
    Java JSON、XML文件/字符串与Bean对象互转解析
    JS实现星级评价
    Spring中@Component注解,@Controller注解详解
    制作滑动条菜单,如何延时处理滑动效果,避免动画卡顿
  • 原文地址:https://www.cnblogs.com/0627zhou/p/9490441.html
Copyright © 2011-2022 走看看