zoukankan      html  css  js  c++  java
  • python全栈开发中级班全程笔记(第三模块、第二章(网络编程))

                python全栈开发第三模块

    第二章:网络编程

    一、计算机基础与网络
      1、一套完整的计算机系统:由三大部分组成:①应用软件 ②系统软件 ③计算机硬件 ###软件依赖系统,系统操作控制硬件###
      2、网络课程目标:让大家写一个 C (client) S (server) 架构软件 (基于网络)
       2.1、什么叫网络:网络是在同一通信区域内通过互联网协议(网络通讯标准),实现各个网络终端的互通互联,从而达到相互通讯传输数据
       简单来讲:网络就是底层的物理连接介质加上互联网协议
       2.2、互联网协议分为以下几种:
       2.21、os七层协议:①应(应用层),②表(表示层),③会(会话层),④传(传输层),⑤网(网络层),⑥数(数据链层),⑦物(物理层)
       2.22、五层协议:前三层(应、表、会)统称为应(应用层)、传(传输层)、网(网络层)、数(数据链层)、物(物理层)
    二、五层协议详解
      1、物理层:传输二进制数据

       2、数据链层:主要任务是按照一定语言标准,把文件转换成指定格式的二进制,交给物理层
      2.1 以太网(Ethernet)MAC 协议(网络中最常见的传输协议)
       其中 MAC 协议中规定:
    ①:一组电信号,称之为组个数据报(或者叫数据帧)
    ②:一组数据报分为报头(head)和数据(data)两部分
    ③:报头固定长度为18个字节(前6个字节是原地址,后6个字节是目标地址)
    ④:规定计算机必须要有网卡,而网卡之上有独一无二的 MAC 地址以供上网使用
       2.2 以太网协议的工作方式:
    基于 MAC 地址的广播工作方式(吼的方式)完成数据的传递,而这种传递方式只局限于局域网
       2.3 小结: 报头head = [原地址 + 目标地址] MAC 协议 = [head(报头) + data(数据)]

       3、网络层:IP 协议
       3.1 IP地址:和以太网协议相同,也是由报头(此处称IP头)与数据组成。
       3.2 IP地址的Arp协议:Arp协议是用来解析IP地址,获得 MAC 地址
         注意:以太网在接收数据后会把 IP头和数据打包成一个数据组再加上以太网的报头进行传输,
      到达服务端后,也会以相反的方法解包,这样形成一个规则:用IP地址 + MAC地址就可找到全世界的每一台机
       4、传输层:
    传输层分为 TCP 和 UDP 两种基于端口的协议,一台机器上的端口有 [0——65535]个,每启动一个软件,都会对应范围内唯一的端口,
    和上面两种网络协议一样,也有自己的数据头和数据组,如果是TCP端口的协议,则数据头以TCP 发送数据,
       4.1 传输原理:
                               [客户端]                            [服务端]
                         [ftp+data]<———打包———[_应用层_]——>[自定义/HTTP/FTP协议]<——[_应用层_]———解包———>[ftp+data]
               {tcp/udp+[ftp+data]}<———打包———[_传输层_]—————>【TCP/UCP协议】<—————[_传输层_]———解包———>{tcp/udp+[ftp+data]}
          {IP+{tcp/udp+[ftp+data]}}<———打包———[_网络层_]——————>【IP 协议 】<———————[_网络层_]———解包———>{IP+{tcp/udp+[ftp+data]}}
    {MAC+{IP+{tcp/udp+[ftp+data]}}}<———打包———[_数据层_]——>Ethernet (以太网协议)<—[_数据层_]——解包——>{MAC+{IP+{tcp/udp+[ftp+data]}}}
    {MAC+{IP+{tcp/udp+[ftp+data]}}}转码0101<——[_物理层_]———>转换成二进制(01011)<——[_物理层_]——>0101转码{MAC+{IP+{tcp/udp+[ftp+data]}}}
             *  物理层转换二进制并发包到客户端  *    |                                     |     * 物理层接收二进制并交给服务端 *
                                                |————>发送——>网线(物理链接 ——>接收数据——>|

       4.2 小结:IP + 端口就能标识全世界范围内独一无二的软件,
       服务端:不容易变动,所以会和IP与端口绑定到一起,
       客户端:不会与IP和端口捆绑到一起

       5、应用层:应用层也有自己的协议(HTTP/FTP), 也可自定义协议
     
    三、传输层详解
      1、站在网络应用开发软件层面来讲,五层协议也可分为 2 大类:即 自定义 和 他定义
       1.1 自定义:自己定义应用层
      1.2 他定义:不用自己多参与,只需用就可以
       2、TCP 协议:
       TCP 协议又称流式协议,大致意思就是发包时,把数据包模拟成像水流一样发出去,接收端也一样方式接受,
       这就需要双向都同意,才能建立协议,而这个双向协议需要三次握手才能成功,所以被称为可靠传输协议,传输效率相对低,费流量
       即:① 客户端:发 SYN=1(建立连接请求信息)SEQ=X(数据包编号信息) 给服务端,
       ② 服务端:同意后发送ACK=1+X (代表同意发送数据包的确认信息)并发送 SYN=1 与 SEQ=Y 信息回给客户端,
        ③ 客户端:同意服务端请求信息 ACK=1+Y
    连接网络成功
    协议通过以后才能正常发送信息,消息发完之后,需断开连接断开连接需要发四次消息确认信息(客户端:同意并接收,服务端:同意并接收)
    即: ① 客户端:数据发送完毕时,发 FIN=1(断开连接请求信息)
    ② 服务端:确认并回复 ACK=1 但不能同时发送断开连接请求,因为这个过程中,有可能自己还没发送完信息
    ③ 服务端:数据传输完毕后,才能发送 FIN=1 的断开连接请求
    ③ 客户端:确认并回复 ACK=1
    断开网络成功

      3、UDP 协议:
    UDP协议也是基于端口建立的,只不过与TCP协议相比,它没有通道,也不需要双向同意信息,简单说,就是单方面的行动,自己的动作不需要别人的同意
    所以,UDP协议不可靠,但是传输效率更高,节约流量,打广告时,这个协议是一个不错的选择

    四、socket 详解
        socket (套接字)应用层与TCP/IP协议通信中的中间软件抽象层,他是一组接口,在设计模式中把复杂的TCP/IP协议整合包装成简单的接口,以供调用
       1、socket(套接字)2大家族
           1.AF_UNIX: 在Unix系统内,一切皆文件,基于文件底层的系统读取数据,2个套接字进程运行在一台机器上,通过访问同一个文件系统,间接通讯
           2.AF_INET:被应用于 IPV 6 还有其他的地址家族,在 python 的网络编程界,经常用到 AF_INET
        2、基于 C(client) S(server ) 架构的套接字实例:
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    # 1.买手机,
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 插卡打电话
    Phone.connect(("127.0.0.1", 3378))
    
    # 发收消息
    Phone.send("hello my name is dragon will".encode("utf-8"))
    Phone.send("will Loong age is 18".encode("utf-8"))
    data1 = Phone.recv(1024)
    data = Phone.recv(1024)
    print(data, data1)
    
    # 挂电话
    Phone.close()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    # 1.买手机,
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)       # AF_INET 定义网络编程的家族,定义数据格式的方法(流式协议)
    # print (Phone)
    
    # 2.插卡,调用对象并绑定
    Phone.bind(("127.0.0.1", 3378))         # 绑定服务端默认地址和端口(0-65535)其中 0-1024 是给操作系统使用, 1024以后才属于其他软件使用
    
    # 3.开机
    Phone.listen(5)
    
    # 4.连接
    conn, client_addr = Phone.accept()
    
    # 5.传数据( 收发消息)
    data = conn.recv(1024)  # 单位是 bytes,1024是最大限制
    print("客户数据:%s" % data)
    conn.send(data.upper())
    
    # 6.挂电话
    conn.close()
    
    
    # 7.关机
    Phone.close()
    server
    
    
    
    五、制作简单有趣的聊天编程
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        msg = input(">>>:").strip()
        Phone.send(msg.encode("utf-8"))
        data = Phone.recv(1024)
        print(data.decode("utf-8"))
        if msg == "q":
            break
    Phone.close()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    conn, client_addr = Phone.accept()
    
    while True:   # 通信循环
        data = conn.recv(1024)
        print(data.decode("utf-8"))
        sdt = input("<<<:").strip()
        conn.send(sdt.encode("utf-8"))
        if sdt == "q":
            break
    
    conn.close()
    
    
    Phone.close()
    server
    六、BUG 修复的相关操作
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        msg = input(">>>:").strip()
        # TODO 解决消息收发为空或空格
        if msg is None:continue
        Phone.send(msg.encode("utf-8"))
        data = Phone.recv(1024)
        print(data.decode("utf-8"))
        if msg == "q":
            break
    Phone.close()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # TODO 为服务端修复BUG:重复使用软件端口编号地址
    Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    conn, client_addr = Phone.accept()
    
    while True:   # 通信循环
        try:         # TODO 针对 Windows系统 解决处理一方断线或退出程序导致死循环或异常报错
            data = conn.recv(1024)
            if not data:break     # TODO 针对 Linux系统   处理异常方法:
            print(data.decode("utf-8"))
            sdt = input("<<<:").strip()
            conn.send(sdt.encode("utf-8"))
            if sdt == "q":
                break
        except ConnectionResetError:       # TODO 注意用捕捉错误的语法
            break
    conn.close()
    
    
    Phone.close()
    server
    七、实现一个服务端与多个客户端交互
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        msg = input(">>>:").strip()
        if msg is None:continue
        Phone.send(msg.encode("utf-8"))
        data = Phone.recv(1024)
        print(data.decode("utf-8"))
        if msg == "q":
            break
    Phone.close()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        msg = input(">>>:").strip()
        if msg is None:continue
        Phone.send(msg.encode("utf-8"))
        data = Phone.recv(1024)
        print(data.decode("utf-8"))
        if msg == "q":
            break
    Phone.close()
    client 1
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        msg = input(">>>:").strip()
        if msg is None:continue
        Phone.send(msg.encode("utf-8"))
        data = Phone.recv(1024)
        print(data.decode("utf-8"))
        if msg == "q":
            break
    Phone.close()
    client 2
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        msg = input(">>>:").strip()
        if msg is None:continue
        Phone.send(msg.encode("utf-8"))
        data = Phone.recv(1024)
        print(data.decode("utf-8"))
        if msg == "q":
            break
    Phone.close()
    client 3
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    
    #
    # TODO  实现一个服务端对应多个客户端 ,需要在接收链接时,加上链接循环
    import socket
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    while True:
        # TODO 加上链接循环
        conn, client_addr = Phone.accept()
    
        while True:   # 通信循环
            try:
                data = conn.recv(1024)
                if not data:break
                print(data.decode("utf-8"))
                sdt = input("<<<:").strip()
                conn.send(sdt.encode("utf-8"))
                if sdt == "q":
                    break
            except ConnectionResetError:
                break
        conn.close()
    
    Phone.close()
    server
    
    
    
    八、模拟ssh远程协助执行命令
    #!User57098My_pycharm_info
    # -*- coding: utf-8 -*-
    # @Time    : 2019/8/3 14:54
    # @Author  : Dragon will
    # @File    : 补充知识点.py
    # @Software: python学习模块
    
    
    # TODO
    #  双平台操作系统的相应指令解析
    #  windows平台         对应的指令                            linux 平台
    #  dir :             查看一个文件夹下的子文件与文件名        ls
    #  ipconfig:         查看本地网卡的 IP 信息                 ifconfig
    #  tasklist:         查看运行进程                           ps aux
    
    
    
    # TODO
    #  如何在系统执行并拿到命令结果:
    import subprocess
    obj = subprocess.Popen('dir', shell=True,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)
    
    print(obj.stdout.read().decode("GBK"))
    print(obj.stderr.read().decode("GBK"))
    ssh知识点
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    # TODO  实现一个服务端对应多个客户端 ,需要在接收链接时,加上链接循环
    import socket
    import subprocess
    
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    while True:
        # TODO 加上链接循环
        conn, client_addr = Phone.accept()
    
        while True:   # 通信循环
            try:
                # 收消息
                cmd = conn.recv(1024)
                if not cmd:break
                print(cmd.decode("utf-8"))
                # 执行命令,拿到结果
                obj = subprocess.Popen(cmd.decode("utf-8"), shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
    
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
    
                # 把返回给命令结果
                print(len(stdout)+len(stderr))
                conn.send(stdout+stderr)
                # TODO + 号是一个可以优化的点
            except ConnectionResetError:
                break
        conn.close()
    
    Phone.close()
    server
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        # 发命令
        cmd = input(">>>:").strip()
        if cmd is None:continue
        Phone.send(cmd.encode("utf-8"))
        # 拿结果并打印
        data = Phone.recv(1024)            # TODO 1024 个字节,传输数据大了可能不够用,需要改进
        print(data.decode("GBK"))          # TODO 此处解码,一定遵守用什么编码,就用什么解码
    
    Phone.close()
    client
    九、粘包现象
       1、粘包现象:因为数据的接收限制,导致多个包(多个命令的结果)粘在一起,出现答非所问的乱象,最终程序乱套
        运行一个程序或软件需要的几个硬件:
                         CPU处理器:主要部件,负责处理所有任务
                         内存:存储 CPU 所有数据
                        硬盘:存储被 CPU 处理好或未处理的内存数据
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        # 发命令
        cmd = input(">>>:").strip()
        if cmd is None: continue
        else: ppt = len(cmd)
        Phone.send(str(ppt).encode("utf-8"))
        Phone.send(cmd.encode("utf-8"))
        # 拿结果并打印
        data = Phone.recv(1024)            # TODO 1024 个字节,传输数据大了可能不够用,需要改进
        print(data.decode("GBK"))          # TODO 此处解码,一定遵守用什么编码,就用什么解码
    
    Phone.close()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    # TODO  实现一个服务端对应多个客户端 ,需要在接收链接时,加上链接循环
    import socket
    import subprocess
    
    """先实例化一个对象 Phone"""
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    while True:
        # TODO 加上链接循环
        conn, client_addr = Phone.accept()
    
        while True:   # 通信循环
            try:
                # 收消息
                cmd = conn.recv(1024)
                cmd1 = conn.recv(cmd.decode("utf-8"))
                if not cmd:break
                print(cmd.decode("utf-8"))
                # 执行命令,拿到结果
                obj = subprocess.Popen(cmd.decode("utf-8"), shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
    
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
    
                # 把返回给命令结果
                conn.send(stdout+stderr)
                # TODO + 号是一个可以优化的点
            except ConnectionResetError:
                break
        conn.close()
    
    Phone.close()
    server
      2、解决粘包问题
    (1)解决粘包的方法有几种
    •   把发送和接收包的数据设置更高(但是这种方法很低级不适合现在的场景)
    •    把要发送的数据长度提前通知对方,就是定制固定长度的报头
        (2)struct 模块详解 
    #!User57098My_pycharm_info
    # -*- coding: utf-8 -*-
    # @Time    : 2019/8/4 21:21
    # @Author  : Dragon will
    # @File    : struct模块详解.py
    # @Software: python学习模块
    
    
    import struct
    import json
    
    # TODO
    #  struct 模块是专门负责网络传输打包专用
    
    bys = struct.pack('i', 1077)  # TODO 打包数据成固定长度, 并转换为 bytes 类型
    #  ( 'i' 是一个参数的简称,打包的时候设置什么, 解包就必须要用什么)
    
    
    obj = struct.unpack('i', bys)  # TODO 解包数据 并接转换为 tuple(元祖)类型
    print(obj[0])
    
    
    
    
    
    # TODO 语法参数括号内值的详解
    # bys = struct.pack('i', 1077)   # 这个括号内的参数,只能有4位数,但是现实中的详细信息远远不止4位数那么简单,所以不够用
    bys = struct.pack('l', 10755555557)
    obj = struct.unpack('l', bys)
    print(obj)
    
    
    # TODO
    #  小结:
    #    i :表示接受小于4位数的数字格式
    #    l  :表示接受小于8位数的数字格式
    #  用 json 把报头格式化后,再用此方法进行打包报头
    #  可能报头会很大,但是,用 json 把报头包装后,就会变得很简短,再用 struct 模块进行包装就能解决所有的问题了
    #  服务端:报头内容 ————> json格式化 ————> json 格式化后长度 ————> struct.pack 包装先发送发送报头长度 ————> 再发送报头内容 ————> 发送需要内容
    #  客户端: 接收报头长度 ————> struct.unpack 解包获取 ————> 接收报头 json 反解 ————> 读取内容长度 ————> 获取内容
    header_dict = {
        "file_name": "a.txt",
        'md5': 5511222,
        'total_size': 222555}
    header_json = json.dumps(header_dict)
    header_bytes = header_json.encode('utf-8')
    
    struct.pack('i', len(header_bytes))
    struct 模块
     3、解决粘包基础版
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    import subprocess
    import struct
    
    
    # TODO 解决粘包问题基础版(制定报头)
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    while True:
        conn, client_addr = Phone.accept()
        while True:   # 通信循环
            try:
                cmd = conn.recv(8096)
                if not cmd:break
                obj = subprocess.Popen(cmd.decode("utf-8"), shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
    
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
    
                # TODO
                #  第一步、制作固定长度的报头
                total_size = len(stdout)+len(stderr)
                header = struct.pack('i', total_size)
                # TODO
                #  第二步,把数据报头发送给客户段
                conn.send(header)
                # TODO
                #  第三步、把数据发送过去
                conn.send(stdout)
                conn.send(stderr)
    
            except ConnectionResetError:
                break
        conn.close()
    
    Phone.close()
    server
       4、解决粘包终极版  
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    
    
    import socket
    import struct
    import json
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        # 发命令
        cmd = input(">>>:").strip()
        if not cmd: continue
        Phone.send(cmd.encode("utf-8"))
        # 拿结果并打印
        # TODO
        #  第一步:拿到报头长度
        obj = Phone.recv(4)
        header_size = struct.unpack('i', obj)[0]
        # TODO
        #  第二步:再收报头
        header_bytes = Phone.recv(header_size)
        # TODO
        #  第三步:从报头中解析数据描述信息(解包)
        header_json = header_bytes.decode("GBK")
        header_dict = json.loads(header_json)
        print(header_dict)
        total_size = header_dict["total_size"]
    
        # TODO
        #  第四步:拿到真实数据
        recv_size = 0
        recv_data = b''
        while recv_size < total_size:
            res = Phone.recv(1024)
            recv_data += res
            recv_size += len(res)
        print(recv_data.decode("GBK"))          # TODO 此处解码,一定遵守用什么编码,就用什么解码
    
    Phone.close()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    import subprocess
    import struct
    import json
    import os
    
    
    # TODO 文件上传下载功能
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    Phone.bind(("127.0.0.1", 3378))
    Phone.listen(5)
    while True:
        conn, client_addr = Phone.accept()
        while True:   # 通信循环
            try:
                # 收到命令
                res = conn.recv(8096)   # b'get a.txt'
                if not res:break
                # 解析命令提取重要命令参数
                cmds = res.decode("GBK").split()     # ['gat', 'a.txt']
                file_name = cmds[1]
                # 以读的方式打开文件,读取文件内容,传给客户端
                # TODO
                #  第一步、制作固定长度的报头:用字典比较合适
                header_dict = {
                    "file_name": file_name,
                    'md5': 5511222,
                    'file_size': os.path.getsize(file_name)}
                header_json = json.dumps(header_dict)
                header_bytes = header_json.encode("utf-8")
                # TODO
                #  第二步先发送报头的长度
                conn.send(struct.pack('i', len(header_bytes)))
                # TODO
                #  第三步,把报头发过去
                conn.send(header_bytes)
                # TODO
                #  第四步、把数据发送过去
                with open(file_name, 'rb') as f:
                    # conn.send(f.read())
                    for line in f:
                        conn.send(line)
            except ConnectionResetError:
                break
        conn.close()
    
    Phone.close()
    server
    十、文件传输(上传与下载)
        上传与下载相同,无非是客户端与服务端的功能实现互相的调换
      1、下载(基于同一目录下的操作代码)
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    
    
    import socket
    import struct
    import json
    
    download_dir = r'C:My_pycharm_infopython学习第三模块(面向对象与网络编程基础)第2章:网络编程9.文件传输下载clentdownload'
    # TODO 上面的内存地址应该写入配置文件
    Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    Phone.connect(("127.0.0.1", 3378))
    while True:
        # 发命令
        cmd = input(">>>:").strip()
        if not cmd: continue
        Phone.send(cmd.encode("utf-8"))
        # 拿结果并打印
        # TODO
        #  第一步:拿到报头长度
        obj = Phone.recv(4)
        header_size = struct.unpack('i', obj)[0]
        # TODO
        #  第二步:再收报头
        header_bytes = Phone.recv(header_size)
        # TODO
        #  第三步:从报头中解析数据描述信息(解包)
        header_json = header_bytes.decode("GBK")
        header_dict = json.loads(header_json)
        print(header_dict)
        total_size = header_dict["file_size"]
        file_name = header_dict['file_name']
        # TODO
        #  第四步:拿到真实数据
        with open('%s/%s' % (download_dir, file_name), 'wb') as f:
            recv_size = 0
    
            while recv_size < total_size:
                line = Phone.recv(1024)
                f.write(line)
                recv_size += len(line)
                # todo 代替进度条功能
                print("总大小:%s  已下载:%s" % (total_size, recv_size))
    
    Phone.close()
    client
    server
     2、用函数实现下载功能
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    
    
    import socket
    import struct
    import json
    
    download_dir = r'C:My_pycharm_infopython学习第三模块(面向对象与网络编程基础)第2章:网络编程9.文件传输用函数实现文件传输clentdownload'
    
    def get(Phone, cmds):
        obj = Phone.recv(4)
        header_size = struct.unpack('i', obj)[0]
        header_bytes = Phone.recv(header_size)
        header_json = header_bytes.decode("GBK")
        header_dict = json.loads(header_json)
        print(header_dict)
        total_size = header_dict["file_size"]
        file_name = header_dict['file_name']
        with open('%s/%s' % (download_dir, file_name), 'wb') as f:
            recv_size = 0
    
            while recv_size < total_size:
                line = Phone.recv(1024)
                f.write(line)
                recv_size += len(line)
                print("总大小:%s  已下载:%s" % (total_size, recv_size))
    
    
    def put():
        """此处需要增加一个上传功能"""
        pass
    
    
    def run():
        Phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        Phone.connect(("127.0.0.1", 3378))
        while True:
            inp = input(">>>:").strip()
            if not inp: continue
            Phone.send(inp.encode("utf-8"))
            cmds = inp.split()
            if cmds[0] == 'get':
                get(Phone, cmds)
            elif cmds[0] == 'put':
                put(Phone, cmds)
        Phone.close()
    
    
    if __name__ == '__main__':
        run()
    client
    # -*-coding: utf-8-*-
    # Author:Dragon Will
    
    import socket
    import subprocess
    import struct
    import json
    import os
    
    
    # 函数版文件传输
    
    share_dir = r'C:My_pycharm_infopython学习第三模块(面向对象与网络编程基础)第2章:网络编程9.文件传输用函数实现文件传输servershare'
    
    
    def get(conn, cmds):
        file_name = cmds[1]
        header_dict = {
            "file_name": file_name,
            'md5': 5511222,
            'file_size': os.path.getsize('%s/%s' % (share_dir, file_name))}
        header_json = json.dumps(header_dict)
        header_bytes = header_json.encode("utf-8")
        conn.send(struct.pack('i', len(header_bytes)))
        conn.send(header_bytes)
        with open('%s/%s' % (share_dir, file_name), 'rb') as f:
            for line in f:
                conn.send(line)
    
    
    def put(conn, cmds):
        """此处需要增加一个上传功能"""
        pass
    
    
    def run():
        Phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        Phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        Phone.bind(("127.0.0.1", 3378))
        Phone.listen(5)
        while True:
            conn, client_addr = Phone.accept()
            while True:   # 通信循环
                try:
                    res = conn.recv(8096)   # b'get a.txt'
                    if not res:break
                    cmds = res.decode("GBK").split()     # ['gat', 'a.txt']
                    if cmds[0] == "get":
                        get(conn, cmds)
                    elif cmds[0] == "put":
                        put(conn, cmds)
    
                except ConnectionResetError:
                    break
            conn.close()
        Phone.close()
    
    if __name__ == '__main__':
        run()
    server
       3、用类实现文件传输
    #!User57098My_pycharm_info
    # -*- coding: utf-8 -*-
    # @Software: python学习模块  
    # @Time    : 2019/8/5 16:02
    # @File    : Client.py
    # @Author  : Dragon will
    
    
    import socket
    import struct
    import subprocess
    import json
    import os
    
    
    class MYTCPClient:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        allow_reuse_address = False
        max_packet_size = 8192
        coding = 'utf-8'
        request_queue_size = 5
        server_dir = 'file_upload'
    
        def __init__(self, server_address, connect=True):
            self.server_address = server_address
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
            if connect:
                try:
                    self.client_connect()
                except:
                    self.client_close()
                    raise
    
        def client_connect(self):
            self.socket.connect(self.server_address)
    
        def client_close(self):
            self.socket.close()
    
        def run(self):
            while True:
                inp = input('>>>:').strip()
                if not inp:continue
                l = inp.split()
                cmd = l[0]
                if hasattr(self, cmd):
                    func = getattr(self, cmd)
                    func(l)
    
        def put(self, args):
            cmd = args[0]
            filename = args[1]
            if not os.path.isfile(filename):
                print('file:%s is not exists' % filename)
                return
            else:
                filesize = os.path.getsize(filename)
    
            head_dic = {'cmd': cmd, 'filename': os.path.basename(filename), 'filesize': filesize}
            print(head_dic)
            head_json = json.dumps(head_dic)
            head__json_bytes = bytes(head_json, encoding=self.coding)
            head_struct = struct.pack('i', len(head__json_bytes))
            self.socket.send(head_struct)
            self.socket.send(head__json_bytes)
            send_size = 0
            with open(filename, 'rb') as f:
                for line in f:
                    self.socket.send(line)
                    send_size += len(line)
                else:
                    print('upload successful')
    
    
    tcpserver1 = MYTCPClient(('127.0.0.1', 8002))
    tcpserver1.run()
    client
    #!User57098My_pycharm_info
    # -*- coding: utf-8 -*-
    # @Software: python学习模块  
    # @Time    : 2019/8/5 16:01
    # @File    : Server.py
    # @Author  : Dragon will
    
    
    import socket
    import struct
    import subprocess
    import json
    import os
    
    
    class MYTCPServer:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        allow_reuse_address = False
        max_packet_size = 8192
        coding = 'utf-8'
        request_queue_size = 5
        server_dir = 'file_upload'
    
        def __init__(self, server_address, bind_and_activate=True):
            self.server_address = server_address
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
    
            if bind_and_activate:
                try:
                    self.server_bind()
                    self.server_activate()
                except:
                    self.server_close()
                    raise
    
        def server_bind(self):
            if self.allow_reuse_address:
                self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind(self.server_address)
            self.server_address = self.socket.getsockname()
    
        def server_activate(self):
            self.socket.listen(self.request_queue_size)
    
        def server_close(self):
            self.socket.accept()
    
        def get_request(self):
            return self.socket.accept()
    
        def close_request(self, request):
            request.close()
    
        def run(self):
            while True:
                self.conn, self.client_addr = self.get_request()
                print('from client', self.client_addr)
                while True:
                    try:
                        head_struct = self.conn.recv(4)
                        if not head_struct:break
    
                        head_len = struct.unpack('1', 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 put(self, args):
            file_path = os.path.normpath(os.path.join(
                self.server_dir,
                args['filename']
            ))
    
            filesize = args['filesize']
            recv_size = 0
    
            print('------>', file_path)
            with open(file_path, 'wb') as f:
                while recv_size < filesize:
                    recv_data = self.conn.recv(self.max_packet_size)
                    f.write(recv_data)
                    print('recvsize:%s filesize:%s' % (recv_size, filesize))
    
    
    tcpserver1 = MYTCPServer(('127.0.0.1', 8002))
    tcpserver1.run()
    server
    十一、udp协议的套接字
    #!User57098My_pycharm_info
    # -*- coding: utf-8 -*-
    # @Software: python学习模块  
    # @Time    : 2019/8/7 21:06
    # @File    : Server.py
    # @Author  : Dragon will
    
    from socket import *
    
    
    server = socket(AF_INET, SOCK_DGRAM)     # SOCK_DGRAM  数据报协议,没有粘包问题
    server.bind(('127.0.0.1', 8080))
    while True:
        data, client_addr = server.recvfrom(1024)
        # server.sendto(data.upper(), client_addr)
        print(data.decode('utf-8'))
        cmd = input(">>>:").strip()
        server.sendto(cmd.encode('utf-8'), client_addr)
        if cmd == 'q':
            break
    
    
    server.close()
    server
    #!User57098My_pycharm_info
    # -*- coding: utf-8 -*-
    # @Software: python学习模块  
    # @Time    : 2019/8/7 21:06
    # @File    : Client.py
    # @Author  : Dragon will
    
    
    # todo 基于 UDP协议的操作
    from socket import *
    
    
    client = socket(AF_INET, SOCK_DGRAM)     # SOCK_DGRAM  数据报协议,没有粘包问题
    while True:
        msg = input(">>>:").strip()
        client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
        data, server_addr = client.recvfrom(1024)
        if data.decode('utf-8') == 'q':break
        print(data.decode('utf-8'))
    
    client.close()
    
    
    # todo
    #  udp 协议说明:
    #  ① udp协议可以发空,因为它自带报头(IP信息与端口信息)
    #  ② udp协议不会粘包,因为它都是数据报,在 linux 系统,如果服务端发送的数据,接收端接收不完,剩下的数据会直接丢失,
    #  在 windows 系统,服务端收不完客户端数据会报错提示接受信息量太大,接收数据设置不够用
    #  ③ udp 协议的典型(NTP, DNS, QQ) 其他基本上没有可用之处,因为 TCP 比 UDP 协议的安全性高,可靠性强
    client
     十二、FTP作业讲解
    TODO   FTP 项目要求与说明
    1、用户加密认证:创建一个 ini 文件存储用户数据 密码最好用 md5 加密 #
    2、允许多个用户同时登陆:此要求可以先不做,等学完高并发时可以考虑完善
    3、每个用户有自己对应的目录,且只能访问自己的目录:在 ini 文件内设置密码和配置路径
    4、对用户进行磁盘配额,每个用户的应用内存空间都不相同:(此方法可在 ini 文件内设置文件内存空间的数值,在打印时,加上数值的内存单位,再用 os 模块统计用户剩余存储单位)
    5、允许用户在 ftp server 上随意切换目录 # 此功能为选学功能
    6、允许用户查看当前目录文件名(同要求 3 一样)
    7、允许用户传输与下载文件,保证文件一致性:文件传输时,为文件的报头部分 md5 加密,当用户上传或下载时,通过比对 md5 值的结果获得一致性
    8、文件传输过程中,显示进度条
    9、附加功能:支持文件的断点续传
     

        

      
























  • 相关阅读:
    Hihocoder-小Hi的烦恼
    Python包下载与离线安装
    Shell输出颜色设置
    MySQL主从配置
    MySQL初始化与用户配置
    [转]常用 GDB 命令中文速览
    搭建github静态博客
    树莓派上手
    vim安装与配置
    数组,看了你就懂了!
  • 原文地址:https://www.cnblogs.com/guoyilong/p/11335855.html
Copyright © 2011-2022 走看看