zoukankan      html  css  js  c++  java
  • 网络编程------粘包问题以及切换目录

    一. 在py代码中如何调用操作系统的命令:

    使用模块: subprocess

        
    
    r  =  subprocess.Popen('ls',
    
      shell=True,
    
      stdout=subprocess.PIPE,
    
      stderr=subprocess.PIPE)
    
    
    # 基本样式: subprocess.Popen(cmd,shell=True,subprocess.stdout,subprocess.stderr)
    # cmd : 代表系统命令
    # shell = True   代表这条命令是 系统命令,告诉操作系统,将cmd当成系统命令去执行
    # stdout   是执行完系统命令之后,用于保存结果的一个管道
    # stderr   是执行完系统命令之后,用于保存错误结果的一个管道
    
    
    
    print(r.stdout.read().decode('gbk'))
    
    print(r.stderr.read().decode('gbk'))
    调用系统命令基础代码

    客户端和服务端代码:

    import socket
    
    sk = socket.socket()
    
    sk.connect_ex(('127.0.0.1',8080))
    while 1:
        cmd = input('请输入一个命令>>>')
        sk.send(cmd.encode('utf-8'))
    
        result = sk.recv(102400).decode('gbk')
    
        print(result)
    
    
    sk.close()
    客户端代码
    import socket
    import subprocess
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    
    conn,addr = sk.accept()
    while 1:
        cmd = conn.recv(1024).decode('utf-8')
        r = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        stdout = r.stdout.read()
        stderr = r.stderr.read()
        if stderr:
            conn.send(stderr)
        else:
            conn.send(stdout)
    
    conn.close()
    sk.close()
    服务端代码

    二.粘包问题.

    粘包问题只发生在TCP协议中, UDP协议不会发生.

    1, 发生粘包问题的原因:

    发送端发送数据时, 接收端不知道应该如何接收, 造成的一种数据混乱的现象.

    TCP协议中发生粘包的两种原因:

      合包机制(nagle 算法): 多次连续发送切间隔较小的数据, 进行打包成一块数据传

      送, TCP协议在进行数据传输的时候是没有限制边界, 电脑在接受后不知道如何进

      行划分, 从而发生粘包问题.

      拆包机制: 在发送端,因为受到网卡的MTU限制, 会将大的超过MTU限制的数据进

      行拆分,拆分成多个小数据进行传输, 当传输到目标主机的操作系统层时, 会重新将

      多个小数据合并成元数据,  (同理因为TCP传输的数据没有边界限制, 在最后一次传

      输的据没有达到传输的数据大小后, 会从下一个数据中自行补齐,从而造成粘包)

      # MTU 限制一般是在1500个字节

      注意: 拆包机制和合包机制均发生在发送端.


    使用UDP协议发送数据,UDP不会发生粘包问题, UDP协议本层对一次收发数据大小

    的限制是:

      65535 - ip包头(20) - udp包头(8) = 65507

    站在数据链路层: 因为网卡的MTU一般被限制在了1500, 所以对于数据链路层来说,

    一次收发的数据大小被限制在 1500 - ip包头(20) - udp包头(8) = 1472

    得到结论:

      如果 sendto(num) ( 传输的内容)

      num > 65507的时候,  会报错

      1472<num<65507的时候, 会在数据链路层进行拆包, UDP本身就是不可靠协议, 所以一旦拆包之后, 造成的多个小数据包在网络传输中, 如果丢任何一个, 此次数据传输都会失败.

      num< 1472    的时候是比较理想的状态


    TCP协议粘包问题的解决:

    1, 小文件传输时:

      理念(以文件上传为例子): 先对操作进行一个判断,创建一个字典, 将要进行的操作

    分别添加到字典中,然后使用os模块中的  os.path.bassname(文件名) 获得文件名称,

    同时打开文件将内容读取出来传入到字典中.再然后将字典序列化后传输给服务器

    进行存储.

    # 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,"content":None}
    #     file_path = input("请输入一个绝对路径:")
    #     filename = os.path.basename(file_path)  #  获取文件名
    #     with open(file_path,"r",encoding="utf-8") as f:
    #         content = f.read()
    #     dic["filename"] = filename # 将获取到的文件名添加到字典中
    #     dic["content"] = content  # 将读取到的文件内容添加到字典中
    #     str_dic = json.dumps(dic) # 序列化字典
    #     sk.send(str_dic.encode("utf-8")) # 进行传输.
    #
    # 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(9090).decode("utf-8") # 接收客户端发送来的内容
    # dic = json.loads(str_dic) # 进行反序列化
    # if dic["opt"] == "upload": # 判断进行的操作步骤
    #     filename = "1"+ dic["filename"]
    #     with open(filename,"w",encoding="utf-8") as f: 
    #     # 创建文件并将文件内容写入到文件中
    # f.write(dic["content"]) # elif dic["opt"] == "download": # 判断进行的操作 # pass # # conn.close() # sk.close()

    大文件传输的解决粘包问题:原理:

     原理: 与小文件传输时类似, 只是现在不是直接把文件内容写入字典,而是通过os模块中的

    os.path.getsize() 获得文件大小后, 将文件大小写入字典, 先将字典序列化后传输给服务器,服

    务器接收后, 根据文件大小,持续写入到文件中.(服务器传送的内容大小应和服务器端写入的时候的大小是对等的.)

    服务端代码:

    import socket
    import os
    import json
    import struct
    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("请输入一个绝对路径:")# 文件的绝对路径
        # E:Python S14day32实现大文件的传输11.mp4
        filename = os.path.basename(file_path)# 文件名字
        filesize = os.path.getsize(file_path)# 获取用户输入的路径中文件的大小
        dic["filename"] = filename
        dic["filesize"] = filesize
        str_dic = json.dumps(dic)
        len_dic = len(str_dic)# 获取到字典的长度,是一个int类型的数据   46   146
        b_len_dic = struct.pack('i',len_dic)# 用一个4bytes的数据表示字典的长度
    
        sk.send(b_len_dic + str_dic.encode("utf-8"))# 将bytes类型的字典的长度 + bytes类型的字典的内容,一起发送给服务器
    
    
        #  因为上边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
    import struct
    sk = socket.socket()
    sk.bind(("127.0.0.1",8001))
    sk.listen()
    conn,addr = sk.accept()
    b_len_dic = conn.recv(4)
    len_dic = struct.unpack('i',b_len_dic)[0]# 获取到int类型字典的长度,
    # unpack得到的是一个元组,要取下标为0的位置
    str_dic = conn.recv(len_dic).decode('utf-8')
    # 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()

    知识补充:

    文件目录的切换:

    import socket
    import os
    
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    abs_path = input('请输入您的根目录:')
    sk.send(abs_path.encode('utf-8')) # 将路径栓输给服务端
    current_dir = sk.recv(1024).decode('utf-8') # 接收服务端返回的当前根目录下的所有内容
    print(current_dir.split('--')) # 将接收到的文件夹以及文件打印出来.
    
    while 1: 
    # 循环体, 编写 需要进行的操作 cmd
    = input('请输入>>>') # cd + 文件夹 .. if cmd == '..': sk.send(cmd.encode('utf-8')) current_dir = sk.recv(1024).decode('utf-8') print(current_dir.split('--')) if cmd == 'cd': filename = input('请输入一个文件夹名:') sk.send((cmd+' '+filename).encode('utf-8')) current_dir = sk.recv(1024).decode('utf-8') print(current_dir.split('--'))

    切换目录服务端代码:

    import socket
    import os
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    conn,addr = sk.accept()
    
    def send_data(conn,path):
        '''你给我一个目录,我把目录发给client'''
        lis_dir = os.listdir(path)
        str_dir = '--'.join(lis_dir)
        conn.send(str_dir.encode('utf-8'))
    
    abs_path = conn.recv(1024).decode('utf-8')# 获取用户输入的绝对路径
    current_dir = abs_path + '/'
    # 以下再处理,都要根据当前路径去处理,无论是返回上一层,还是进入下一层 send_data(conn,current_dir)# 把用户输入的路径下的所有文件及文件夹返回给客户端 # C:/Program Files (x86)/Common Files while 1: cmd = conn.recv(1024).decode('utf-8') if cmd == '..': current_dir = current_dir.split('/')[:-2] current_dir = '/'.join(current_dir)+'/' # if 如果当前是C盘: # 就返回给客户端告诉说没有上一层了! send_data(conn, current_dir) else: filename = cmd.split(' ')[1]# 获取用户输入的文件名字 current_dir += filename + '/'
    # 将文件名字添加到当前路径下,组成一个完整的新路径 if os.path.isdir(current_dir):# 如果客户输入的文件名字是一个文件夹 send_data(conn, current_dir) else:# 如果不是一个文件夹 conn.send(b'bu shi wen jian jia') # conn.close() # sk.close()

     

  • 相关阅读:
    python进阶(1)--多进程与多线程概念
    mysql数据库(5)--表的相关操作
    mysql数据库(4)--表删除操作
    备注
    mysql数据库(3)--mysql的执行顺序
    mysql数据库(2)--窗口函数之序号函数
    Spring MVC 返回中文乱码
    郁闷,郁闷啊
    Struts2 下载文件
    js文件加载
  • 原文地址:https://www.cnblogs.com/hfbk/p/9506668.html
Copyright © 2011-2022 走看看