zoukankan      html  css  js  c++  java
  • 仿优酷项目

    可参考

    https://www.cnblogs.com/Dominic-Ji/p/10897142.html

    1.项目需求分析:

    - 管理员
      1 注册
      2 登录
      3 上传视频
      4 删除视频
      5 发布公告

    - 用户
      1 注册
      2 登录
      3 冲会员
      4 查看视频
      5 下载免费视频
      6 下载收费视频
      7 查看下载记录
      8 查看公告

    2.程序的架构设计

    架构图:

        - 三层架构
            - 用户视图层
                与用户交互的
    
            - 接口层
                处理核心业务逻辑
    
            - 数据层
                对数据进行存取
    
                - ATM:
                    - 存: 序列化
                        dict ----> json ----> data.json
                    - 取: 反序列化
                        data.json ----> json ----> dict
    
                    - 优点:
                        可跨平台
    
                    - 缺点:
                        不能存对象
    
                - 选课系统
                    - 存: 序列化
                        object ----> pickle(bytes) ----> data.pickle
    
                    - 取: 反序列化
                        data.pickle ----> pickle(bytes) ----> object
    
                    - 优点:
                        可以存对象
    
                    - 缺点:
                        不可跨平台
    
    
                - 仿优酷系统
                    - ORM:
                        - 存:
                            object ----> MySQL
    
                        - 取:
                            MySQL ----> [{}, {}] ----> [object, object]
                            object.属性
                            object.方法
    
                    - pymysql
                        - cursor.execute('sql语句')
                        # select() 查询数据
                        - object.select()  # ----> select * from ...
                        # save() 插入数据
                        - object.save()  # ----> insert into .....动态 sql
                        # update() 更新数据
                        - object.update()  # ----> update tab set ....
    
                    - MySQL:
                        - json ----> dict
    
                        - dict.属性
                        - dict.get('key')
                        - dict['key']

    3.亮代码

    客户端

     conf/settings

    import os
    
    BASE_PATH = os.path.dirname(os.path.dirname(__file__))
    
    UPLOAD_MOVIE_DIR = os.path.join(
        BASE_PATH, 'upload_movie_dir'
    )
    
    DOWNLOAD_MOVIE_DIR = os.path.join(
        BASE_PATH, 'download_movie_dir'
    )
    View Code

    core/admin

    '''
    管理员视图
    '''
    from lib import common
    from conf import settings
    import os
    user_info = {
        'cookies': None
    }
    
    
    # 管理员注册视图
    def register(client):
        while True:
            username = input('请输入用户名: ').strip()
            password = input('请输入密码: ').strip()
            re_password = input('请确认密码: ').strip()
            if password == re_password:
                # 一 组织客户端的注册数据
                send_dic = {
                    'username': username,
                    'password': password,
                    'user_type': 'admin',
                    'func_type': 'register'
                }
    
                back_dic = common.send_and_back(send_dic, client)
    
                # 判断服务端返回来的字典是否注册成功,并打印消息
                if back_dic.get('flag'):
                    print(back_dic.get('msg'))
                    break
    
                else:
                    print(back_dic.get('msg'))
    
    
    # 管理员登录
    def login(client):
        while True:
            username = input('请输入用户名: ').strip()
            password = input('请输入密码: ').strip()
    
            # 1.组织数据发送给服务端
            send_dic = {
                'username': username,
                'password': password,
                'func_type': 'login'
            }
    
            # 调用commin中的send_and_back与服务端交互
            back_dic = common.send_and_back(send_dic, client)
    
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                user_info['cookies'] = back_dic.get('session')
                break
    
            else:
                print(back_dic.get('msg'))
    
    
    # 管理员上传电影
    def upload_movie(client):
        while True:
            upload_movie_dir = settings.UPLOAD_MOVIE_DIR
    
            # 1.打印上传电影目录所有的电影文件,让用户选择
            movie_list = os.listdir(upload_movie_dir)
    
            if not movie_list:
                print('没有可上传的电影!!!')
                break
    
            for index, movie_name in enumerate(movie_list):
                print(index, movie_name)
    
            choice = input('请输入上传电影编号: ').strip()
    
            if choice == 'q':
                break
    
            if not choice.isdigit():
                continue
    
            choice = int(choice)
    
            if choice not in range(len(movie_list)):
                continue
    
            movie_name = movie_list[choice]
    
            # 2.获取真实电影数据
            movie_path = os.path.join(upload_movie_dir, movie_name)
    
            # 3.组织需要上传的电影信息发送给服务端
            movie_size = os.path.getsize(movie_path)
    
            # 4.获取电影md5值,发送给服务端,校验电影是否存在
            movie_md5 = common.get_movie_md5(movie_path)
    
            # 5.先发送检测电影是否存在数据给服务端
            send_dic = {
                'func_type': 'check_movie',
                'movie_md5': movie_md5,
                'cookies': user_info.get('cookies')
            }
    
            back_dic = common.send_and_back(send_dic, client)
    
            # 6.校验back_dic中返回的信息,若电影不存在则,发送上传电影功能字典
            if back_dic.get('flag'):
    
                # 7.管理员设置上传的电影是否免费
                choice2 = input('是否免费(y or n)...').strip()
                is_free = 0
    
                # 判断用户如果输入y 给is_free设置为1
                if choice2 == 'y':
                    is_free = 1
    
                send_dic = {
                    'func_type': 'upload_movie',
                    'movie_name': movie_name,
                    'movie_size': movie_size,
                    'movie_md5': movie_md5,
                    'cookies': user_info.get('cookies'),
                    'is_free': is_free
                }
    
                # 7.发送电影信息字典数据给服务端
                back_dic2 = common.send_and_back(
                    send_dic, client, file=movie_path)
    
                if back_dic2.get('flag'):
                    print(back_dic2.get('msg'))
                    break
            else:
                print(back_dic.get('msg'))
    
    
    # 管理员删除电影
    def delete_movie(client):
        while True:
            # 1.先去服务端获取可删除的电影
            send_dic = {
                'func_type': 'get_movie_list',
                'cookies': user_info.get('cookies'),
                'movie_type': 'all'
            }
    
            back_dic = common.send_and_back(send_dic, client)
    
            if not back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
    
            # 2.打印并选择可以删除的电影
            movie_list = back_dic.get('movie_list')
    
            for index, movie_name_id in enumerate(movie_list):
                print(index, movie_name_id)
            choice = input('请需要删除的电影编号: ').strip()
            if choice == 'q':
                break
            if not choice.isdigit():
                continue
            choice = int(choice)
    
            if choice not in range(len(movie_list)):
                continue
    
            # 3.发送删除的请求给服务端,让服务端修改电影的is_delete字段即可
            # movie_name_id ---> [电影名字, 电影id]
            movie_name_id = movie_list[choice]
    
            send_dic = {
                'func_type': 'delete_movie',
                'cookies': user_info.get('cookies'),
                'movie_id': movie_name_id[1]  # 传入唯一的电影id
            }
    
            back_dic2 = common.send_and_back(send_dic, client)
            if back_dic2.get('flag'):
                print(back_dic2.get('msg'))
                break
    
    
    # 管理员发布公告
    def send_notice(client):
    
        # 1.让管理员输入 公告标题与内容
        title = input('请输入公告标题:').strip()
    
        # 2.发送数据给服务端保存公告即可
        content = input('请输入公告内容:').strip()
    
        send_dic = {
            'cookies': user_info.get('cookies'),
            'title': title,
            'content': content,
            'func_type': 'send_notice'
        }
    
        back_dic = common.send_and_back(send_dic, client)
        print(back_dic.get('msg'))
    
    
    func_dic = {
        '1': register,
        '2': login,
        '3': upload_movie,
        '4': delete_movie,
        '5': send_notice
    }
    
    
    # 管理员视图
    def admin_view(client):
    
        while True:
            print('''
                1 注册
                2 登录
                3 上传视频
                4 删除视频
                5 发布公告
                q.退出
            ''')
    
            choice = input('请输入功能编号:').strip()
            if choice == 'q':
                break
    
            if choice not in func_dic:
                continue
    
            func_dic.get(choice)(client)
    View Code

    core/src

    from core import admin
    from core import user
    from tcp_client import socket_client
    
    
    # 功能字典
    func_dic = {
        '1': admin.admin_view,
        '2': user.user_view
    }
    
    
    # 总视图run函数
    def run():
        print('Client is running...')
        client = socket_client.get_client()
        while True:
            print('''
            1.管理员视图
            2.普通用户视图
            q.退出
            ''')
    
            choice = input('请输入功能编号: ').strip()
    
            if choice == 'q':
                break
    
            if choice not in func_dic:
                continue
    
            func_dic.get(choice)(client)
    
        client.close()
    View Code

    core/user

    from lib import common
    from conf import settings
    import time
    import os
    '''
    用户视图
    '''
    user_info = {
        'cookies': None,
        'is_vip': 0  # 默认登录前不是VIP
    }
    
    
    # 普通用户注册视图
    def register(client):
        while True:
            username = input('请输入用户名: ').strip()
            password = input('请输入密码: ').strip()
            re_password = input('请确认密码: ').strip()
            if password == re_password:
                # 一 组织客户端的注册数据
                send_dic = {
                    'username': username,
                    'password': password,
                    'user_type': 'user',
                    'func_type': 'register'
                }
    
                back_dic = common.send_and_back(send_dic, client)
    
                # 判断服务端返回来的字典是否注册成功,并打印消息
                if back_dic.get('flag'):
                    print(back_dic.get('msg'))
                    break
    
                else:
                    print(back_dic.get('msg'))
    
    
    #  普通用户登录
    def login(client):
        while True:
            username = input('请输入用户名: ').strip()
            password = input('请输入密码: ').strip()
    
            # 1.组织数据发送给服务端
            send_dic = {
                'username': username,
                'password': password,
                'func_type': 'login'
            }
    
            # 调用commin中的send_and_back与服务端交互
            back_dic = common.send_and_back(send_dic, client)
    
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                user_info['cookies'] = back_dic.get('session')
                is_vip = back_dic.get('is_vip')
                if is_vip:
                    user_info['is_vip'] = is_vip
    
                break
    
            else:
                print(back_dic.get('msg'))
    
    
    #  普通用户充会员
    def buy_vip(client):
        # 1.判断当前用户是否是会员
        if not user_info.get('is_vip'):
    
            choice = input('请输入 y or n 确认是否购买 (购买会员5000元): ').strip()
            if choice == 'y':
                # 2.若不是会员则,发送请求让服务端修改is_vip字段
                send_dic = {
                    'func_type': 'buy_vip',
                    'cookies': user_info.get('cookies')
                }
    
                back_dic = common.send_and_back(send_dic, client)
                if back_dic.get('flag'):
                    print(back_dic.get('msg'))
                    login(client)
                else:
                    print(back_dic.get('msg'))
            else:
                print('穷比,枪(gun)吧~')
    
        else:
            print('已经是会员了, 该用户已经是黑金VIP!!')
    
    
    #  普通用户查看电影
    def check_movies(client):
        # 1.先去服务端获取可删除的电影
        send_dic = {
            'func_type': 'get_movie_list',
            'movie_type': 'all',
            'cookies': user_info.get('cookies')
        }
    
        back_dic = common.send_and_back(send_dic, client)
    
        if back_dic.get('flag'):
            print(back_dic.get('movie_list'))
    
    
    #  普通用户下载免费电影
    def download_free_movie(client):
        while True:
            # 1.先往服务端发送请求,获取所有免费电影
            send_dic = {
                'func_type': 'get_movie_list',
                'movie_type': 'free',
                'cookies': user_info.get('cookies')
            }
    
            back_dic = common.send_and_back(send_dic, client)
            if not back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
    
            movie_list = back_dic.get('movie_list')
    
            if not movie_list:
                print('没有可下载的免费电影')
                break
    
            # 2.打印所有免费电影,并打印,让用户选择
            for index, name_id_type in enumerate(movie_list):
                print(index, name_id_type)
    
            choice = input('请输入需要下载电影的编号: ').strip()
            if not choice.isdigit():
                continue
    
            choice = int(choice)
    
            if choice not in range(len(movie_list)):
                continue
    
            movie_name, movie_id, movie_type = movie_list[choice]
            # print(movie_name, movie_id, movie_type)
    
            # 3.选择成功后,发送下载免费电影请求给服务端
            send_dic2 = {
                'func_type': 'download_movie',
                'movie_name': movie_name,
                'movie_id': movie_id,
                'cookies': user_info.get('cookies')
    
            }
    
            back_dic2 = common.send_and_back(send_dic2, client)
    
            # 获取广告时间,并模拟广告等待
            wait_time = back_dic2.get('wait_time')
            if wait_time:
                print('广告时间: {~真情求缘,重金求子~【联系热线: 18574251860】}')
            time.sleep(wait_time)  # 0, 5
    
            # 4.接收服务端返回的下载电影字典
            movie_size = back_dic2.get('movie_size')
    
            # 5.接收电影真实数据,并保存到download_movie_dir中
            movie_path = os.path.join(
                settings.DOWNLOAD_MOVIE_DIR, movie_name
            )
    
            # 接收电影
            recv_data = 0
            with open(movie_path, 'wb') as f:
                while recv_data < movie_size:
                    data = client.recv(1024)
                    f.write(data)
                    recv_data += len(data)
            break
    
    
    #  普通用户下载收费电影
    def download_buy_movie(client):
        while True:
    
            # 1.先往服务端发送请求,获取所有免费电影
            send_dic = {
                'func_type': 'get_movie_list',
                'movie_type': 'pay',
                'cookies': user_info.get('cookies')
            }
            back_dic = common.send_and_back(send_dic, client)
            if not back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
    
            movie_list = back_dic.get('movie_list')
    
            if not movie_list:
                print('没有可下载的收费电影')
                break
    
            # 2.打印所有免费电影,并打印,让用户选择
            for index, name_id_type in enumerate(movie_list):
                print(index, name_id_type)
    
            # 交钱并选择电影, 不给钱不让选择,直接退出
            choice_buy = input('请缴费---》 [普通用户购买电影(500元一部), VIP用户打骨折(50元一部)], 确认购买输入y,否则退出!: ').strip()
            if choice_buy == 'y':
                if user_info.get('is_vip'):
                    print('会员购买成功!')
                else:
                    print('普通用户购买成功!')
    
                print('准备开始选择电影啦~~~~~')
    
            else:
                print('qiong人走吧~~~~~')
                break
    
            choice = input('请输入需要下载电影的编号: ').strip()
            if not choice.isdigit():
                continue
    
            choice = int(choice)
    
            if choice not in range(len(movie_list)):
                continue
    
            # 收费电影的信息
            movie_name, movie_id, movie_type = movie_list[choice]
            # print(movie_name, movie_id, movie_type)
    
            # 3.选择成功后,发送下载免费电影请求给服务端
            send_dic2 = {
                'func_type': 'download_movie',
                'movie_name': movie_name,
                'movie_id': movie_id,
                'cookies': user_info.get('cookies')
    
            }
    
            back_dic2 = common.send_and_back(send_dic2, client)
    
    
            # 收费电影没有广告时间
    
            # 4.接收服务端返回的下载电影字典
            movie_size = back_dic2.get('movie_size')
    
            # 5.接收电影真实数据,并保存到download_movie_dir中
            movie_path = os.path.join(
                settings.DOWNLOAD_MOVIE_DIR, movie_name
            )
    
            # 接收电影
            recv_data = 0
            with open(movie_path, 'wb') as f:
                while recv_data < movie_size:
                    data = client.recv(1024)
                    f.write(data)
                    recv_data += len(data)
            break
    
    
    #  普通用户查看下载记录
    def check_movie_record(client):
    
        send_dic = {
            'func_type': 'check_movie_record',
            'cookies': user_info.get('cookies')
        }
    
        back_dic = common.send_and_back(send_dic, client)
    
        if back_dic.get('flag'):
            print(back_dic.get('download_movie_list'))
    
        else:
            print(back_dic.get('msg'))
    
    
    #  普通用户查看公告
    def check_notice(client):
        # 1.发送查看公告请求给服务端
        send_dic = {
            'func_type': 'check_notice',
            'cookies': user_info.get('cookies')
        }
    
        # 2.服务端返回公告信息,并打印
        back_dic = common.send_and_back(send_dic, client)
        if back_dic.get('flag'):
            print(back_dic.get('notice_list'))
    
        else:
            print(back_dic.get('msg'))
    
    
    func_dic = {
        '1': register,
        '2': login,
        '3': buy_vip,
        '4': check_movies,
        '5': download_free_movie,
        '6': download_buy_movie,
        '7': check_movie_record,
        '8': check_notice
    }
    
    
    # 管理员视图
    def user_view(client):
        while True:
            print('''
                1 注册
                2 登录
                3 冲会员
                4 查看视频
                5 下载免费视频
                6 下载收费视频
                7 查看下载记录
                8 查看公告
                q.退出
            ''')
    
            choice = input('请输入功能编号:').strip()
            if choice == 'q':
                break
    
            if choice not in func_dic:
                continue
    
            func_dic.get(choice)(client)
    View Code

    lib/common

    import struct
    import hashlib
    import json
    import os
    
    
    # 发送与接收
    def send_and_back(send_dic, client, file=None):
        # 1.先序列化,得到json数据,并转成bytes数据
        json_bytes = json.dumps(send_dic).encode('utf-8')
    
        # 2.给bytes数据制作报头
        headers = struct.pack('i', len(json_bytes))
    
        # 二 将打包好的数据发送给服务端
        # 3.先发送报头
        client.send(headers)
        # 4.再发送真实数据 bytes数据
        client.send(json_bytes)
    
        # 注意: 此处上传电影
        if file:
            with open(file, 'rb') as f:
                for line in f:
                    client.send(line)
    
        # 三 接收服务端返回的数据
        # 1.先接收报头
        headers = client.recv(4)
        print(headers, '检测报头是否有数据')
    
        # 2.解压报头,获取真实数据的长度
        # struct.unpack得到一个元组
        bytes_len = struct.unpack('i', headers)[0]
    
        # 3.再接收真实数据
        json_bytes_data = client.recv(bytes_len).decode('utf-8')
    
        # 4.反序列化得到真实数据字典
        back_dic = json.loads(json_bytes_data)
        return back_dic
    
    
    # 获取电影md5值
    def get_movie_md5(movie_path):
        movie_size = os.path.getsize(movie_path)
    
        # 1490  ---> first: 0     2:三分之一    3: 三分之二   last:movie_size - 10
        # 找到指定位置,每个位置截取10个
        # 开始根据电影的长度 截取指定位置的值,并生成md5值
    
        # 注意: 此处是截取电影数据的 4个位置
        index_list = [0, movie_size // 3, (movie_size // 3) * 2, movie_size - 10]
    
        # 想要在电影的4个位置分别截取10个bytes数据 然后做md5加密
        md5_obj = hashlib.md5()
    
        # 先打开电影的真实数据
        with open(movie_path, 'rb') as f:
            for line in index_list:
                # 光标移动到指定位置
                f.seek(line)
    
                # 每个位置获取10个bytes数据
                data = f.read(10)
    
                # 每10个bytes加一次密
                md5_obj.update(data)
    
        return md5_obj.hexdigest()
    View Code

    tcp_client

    import socket
    
    
    # socket客户端 函数
    def get_client():
        client = socket.socket()
        client.connect(
            ('127.0.0.1', 9527)
        )
        return client
    View Code

    start

    import sys
    import os
    sys.path.append(
        os.path.dirname(__file__)
    )
    
    
    # from tcp_client import socket_client
    from core import src
    
    
    if __name__ == '__main__':
    
        src.run()
    View Code

    服务端

     conf/settings

    import os
    
    BASE_PATH = os.path.dirname(
        os.path.dirname(__file__)
    )
    
    
    MOVIE_DIR = os.path.join(BASE_PATH, 'movie_dir')
    View Code

    db/models

    '''
    models.py
        专门用于存放 数据表类
    '''
    from orm_control.orm import Models, IntegerField, StringField
    
    
    # 用户表类: id、用户名、密码、用户类型、是否为VIP、注册时间 字段
    class User(Models):
        u_id = IntegerField(name='u_id', primary_key=True)  # u_id = integerField_obj
        username = StringField(name='username')
        password = StringField(name='password')
        user_type = StringField(name='user_type')  # admin, user
        # 0: 默认不是vip,   1: 是vip
        is_vip = IntegerField(name='is_vip')
        register_time = StringField(name='register_time')
    
    
    # 电影表类: id、电影名字、电影大小、电影md5值、电影是否免费、电影是否删除、上传时间、上传用户的id
    class Movie(Models):
        m_id = IntegerField(name='m_id', primary_key=True)
        movie_name = StringField(name='movie_name')
        movie_size = StringField(name='movie_size')
        movie_md5 = StringField(name='movie_md5')
        # 0: 收费  1:免费
        is_free = IntegerField(name='is_free')
        # 0:未删除  1: 已删除
        is_delete = IntegerField(name='is_delete')
        upload_time = StringField(name='upload_time')
        user_id = IntegerField(name='user_id')
    
    
    # 公告表类: id、公告标题, 公告内容、发布时间、发布用户id
    class Notice(Models):
        n_id = IntegerField(name='n_id', primary_key=True)
        title = StringField(name='title')
        content = StringField(name='content')
        create_time = StringField(name='create_time')
        user_id = IntegerField(name='user_id')
    
    
    # 下载记录表: id、下载电影的id、下载用户的id、下载时间
    class DownloadRecord(Models):
        d_id = IntegerField(name='d_id', primary_key=True)
        movie_id = IntegerField(name='movie_id')
        user_id = IntegerField(name='user_id')
        download_time = StringField(name='download_time')
    View Code

    db/user_data

    user_online = {
        #
        # '用户凭证1': 'session1',
        # '用户凭证1': 'session2',
        # '用户凭证1': 'session3',
        # '用户凭证1': 'session4',
        #
        # '用户凭证2': 'session2',
        # '(127.0.0.1: 9527)': 'session1',
        # '(127.0.0.1: 9528)': 'session2',
    
        # '(127.0.0.1: 9528)': ['session2', '用户u_id'],
    
    }
    View Code

    interface/admin_interface

    from db.models import User
    from db.models import Movie
    from db.models import Notice
    import datetime
    from lib import common
    from db import user_data
    from threading import Lock
    from conf import settings
    import os
    mutex = Lock()
    
    
    # 检测电影接口
    @common.login_auth
    def check_movie_interface(back_dic, conn):
        movie_md5 = back_dic.get('movie_md5')
        # 1.通过电影表查询电影的md5值是否存在
        movie_obj_list = Movie.select_data(movie_md5=movie_md5)
        if movie_obj_list:
            send_dic = {
                'flag': False, 'msg': '电影已存在!'
            }
        else:
            send_dic = {
                'flag': True, 'msg': '可以继续上传!'
            }
    
        # 2.将校验后的send_dic返回给客户端
        common.send_data(send_dic, conn)
    
    
    # 上传电影功能
    @common.login_auth
    def upload_movie_interface(back_dic, conn):
        # 1.组织电影上传的目录
        # 1251982h5219-yhuibwquiog0argwwuiqgr + 电影名字.mp4
        # 保证电影名字唯一
        movie_name = common.get_session() + back_dic.get('movie_name')
    
        movie_path = os.path.join(
            settings.MOVIE_DIR, movie_name
        )
    
        # 2.开始接收客户端上传的电影
        movie_size = back_dic.get('movie_size')
    
        recv_data = 0
        with open(movie_path, 'wb') as f:
    
            while recv_data < movie_size:
                data = conn.recv(1024)
                f.write(data)
                recv_data += len(data)
    
        # 3.数据保存电影上传数据
        movie_obj = Movie(
            movie_name=movie_name,
            movie_size=movie_size,
            movie_md5=back_dic.get('movie_md5'),
            is_free=back_dic.get('is_free'),
            is_delete=0,
            upload_time=str(datetime.datetime.now()),
            # 传当前登录用户的id
            user_id=back_dic.get('user_id')  # 在装饰器里面获取的user_id
        )
    
        movie_obj.insert_data()
    
        send_dic = {
            'flag': True, 'msg': '电影上传成功!'
        }
    
        common.send_data(send_dic, conn)
    
    
    # 删除电影接口
    @common.login_auth
    def delete_movie_interface(back_dic, conn):
    
        movie_id = back_dic.get('movie_id')
    
        # 1.获取数据库中电影的记录
        movie_obj = Movie.select_data(m_id=movie_id)[0]
    
        # 2.修改电影的 is_delete字段为1
        movie_obj.is_delete = 1
    
        # 3.调用update_data更新delete字段
        movie_obj.update_data()
    
        send_dic = {
            'flag': True,
            'msg': '电影删除成功!'
        }
    
        common.send_data(send_dic, conn)
    
    
    # 发布公告接口
    @common.login_auth
    def send_notice_interface(back_dic, conn):
        notice_obj = Notice(
            title=back_dic.get('title'),
            content=back_dic.get('content'),
            create_time=str(datetime.datetime.now()),
            user_id=back_dic.get('user_id')
        )
    
        notice_obj.insert_data()
    
        send_dic = {
            'msg': '公告发布成功!'
        }
    
        common.send_data(send_dic, conn)
    View Code

    interface/common_interface

    from lib import common
    from db.models import Movie, User
    import datetime
    from db.user_data import user_online
    from threading import Lock
    from conf import settings
    import os
    from db.models import DownloadRecord
    mutex = Lock()
    
    
    # 注册接口
    def register_interface(back_dic, conn):
        # 业务逻辑的处理
        # 1.判断用户是否存在,去数据库中查找,ORM,User
        username = back_dic.get('username')
    
        user_obj_list = User.select_data(username=username)  # [obj, obj, obj]
        if user_obj_list:
            # 将用户已存在的数据,打包发送回给客户端
            send_dic = {
                'flag': False, 'msg': '用户已存在!'
            }
        else:
    
            # 开始插入数据
            user_obj = User(
                username=username,
                password=common.get_md5(back_dic.get('password')),
                user_type=back_dic.get('user_type'),
                register_time=str(datetime.datetime.now())
            )
    
            user_obj.insert_data()
    
            send_dic = {
                'flag': True, 'msg': f'用户: {username} 注册成功!'
            }
    
        common.send_data(send_dic, conn)
    
    
    # 登录接口
    def login_interface(back_dic, conn):
        # 1.判断用户是否存在
        user_obj_list = User.select_data(username=back_dic.get('username'))
    
        if not user_obj_list:
            send_dic = {
                'flag': False, 'msg': '用户不存在!'
            }
        else:
    
            # 校验密码是否正确
            user_obj = user_obj_list[0]
    
            if user_obj.password == common.get_md5(back_dic.get('password')):
                addr = back_dic.get('addr')
    
                # 1.产生一个session随机字符串
                session = common.get_session()
                # (127.0.0.1: 9527)
                # 加锁
                mutex.acquire()
                user_online[addr] = [session, user_obj.u_id]  # [seesion, u_id]
                mutex.release()
    
                send_dic = {
                    'flag': True, 'msg': '登录成功!', 'session': session
                }
    
                if user_obj.is_vip:
                    send_dic['is_vip'] = user_obj.is_vip
    
            else:
                send_dic = {
                    'flag': False, 'msg': '密码错误!'
                }
    
        common.send_data(send_dic, conn)
    
    
    # 未删除电影接口
    @common.login_auth
    def get_movie_list_interface(back_dic, conn):
        # 1.查看数据库中所有的未删除电影
        movie_obj_list = Movie.select_data()
        if movie_obj_list:
            # 2.过滤已删除的电影
            # 可删除电影list
            movie_list = []
    
            # 遍历所有电影
            for movie_obj in movie_obj_list:
                # 过滤掉所有已删除的电影
                if not movie_obj.is_delete:
                    if back_dic.get('movie_type') == 'all':
                        # 将所有未删除的电影名字 添加到movie_list中
                        movie_list.append(
                            # [电影名字, 电影的id, 电影是否 免费收费
                            [movie_obj.movie_name, movie_obj.m_id, '免费' if movie_obj.is_free else '收费']
                        )
    
                    elif back_dic.get('movie_type') == 'free':
                        # 判断当前电影对象如果is_free中有值,证明都是免费电影
                        if movie_obj.is_free:
                            movie_list.append(
                                # [电影名字, 电影的id, 电影是否 免费收费
                                [movie_obj.movie_name, movie_obj.m_id, '免费']
                            )
                    else:  # pay
                        # 获取所有收费电影
                        if not movie_obj.is_free:
                            movie_list.append(
                                # [电影名字, 电影的id, 电影是否 免费收费
                                [movie_obj.movie_name, movie_obj.m_id, '收费']
                            )
    
            send_dic = {
                'flag': True, 'movie_list': movie_list
            }
    
        else:
            send_dic = {
                'flag': False, 'msg': '服务端没有电影'
            }
    
        common.send_data(send_dic, conn)
    
    
    # 下载电影接口
    @common.login_auth
    def download_free_movie_interface(back_dic, conn):
        user_id = back_dic.get('user_id')
        user_obj = User.select_data(u_id=user_id)[0]
    
        # 1.先获取当前电影的电影名字
        movie_name = back_dic.get('movie_name')
    
        # 2.获取电影的存放路径
        movie_path = os.path.join(
            settings.MOVIE_DIR, movie_name
        )
    
        # 3.获取电影的大小,与电影的信息,发送给客户端
        send_dic = {
            'movie_size': os.path.getsize(movie_path),
            'wait_time': 0
        }
    
        # 4.判断当前用户是否是VIP,若不是则添加广告时间 5s
        if not user_obj.is_vip:
            send_dic['wait_time'] = 5
    
        # 5.发送真实电影数据
        common.send_data(send_dic, conn, file=movie_path)
    
        # 6.将下载记录插入到 记录表中
        download_obj = DownloadRecord(
            movie_id=back_dic.get('movie_id'),
            user_id=user_id,
            download_time=str(datetime.datetime.now())
        )
    
        download_obj.insert_data()
    View Code

    interface/user_interface

    from lib import common
    from db.models import User, DownloadRecord, Movie, Notice
    
    
    # 购买会员接口
    @common.login_auth
    def buy_vip_interface(back_dic, conn):
        user_id = back_dic.get('user_id')
        user_obj = User.select_data(u_id=user_id)[0]
        user_obj.is_vip = 1
        user_obj.update_data()
    
        send_dic = {
            'flag': True, 'msg': '充值购买成功!'
        }
    
        common.send_data(send_dic, conn)
    
    
    # 下载记录接口
    @common.login_auth
    def check_movie_record_interface(back_dic, conn):
        # 1.先获取当前用户id
        user_id = back_dic.get('user_id')
    
        # 然后根据id获取当前用户所有的下载记录
        download_obj_list = DownloadRecord.select_data(user_id=user_id)
    
        if not download_obj_list:
            send_dic = {
                'flag': False, 'msg': '没有下载记录'
            }
    
        else:
    
            download_movie_list = []
    
            # 方式一:
            # 2.循环遍历所有的下载记录,获取每一步电影的id
            # for download_obj in download_obj_list:
            #
            #     movie_id = download_obj.movie_id
            #     # 循环几次,就查询几次数据,操作几次数据库;
            #     # ---》 可以改成,先查询所有的电影,在根据电影的movie_id做比对,减少对数据库的操作;
            #     movie_obj = Movie.select_data(m_id=movie_id)[0]
            #
            #     # 将电影的名字与下载记录的时间,添加到download_movie_list中,用户返回给客户端展示给用户看的
            #     download_movie_list.append(
            #         [movie_obj.movie_name, download_obj.download_time]
            #     )
    
    
            # 方式二:
            # 只查一次数据, 操作一次数据库
            # 将所有电影查出来
            movie_obj_list = Movie.select_data()
    
            # 遍历所有 “下载电影记录对象” download_obj
            for download_obj in download_obj_list:
    
                # 获取所有下载记录的电影id
                download_movie_id = download_obj.movie_id
    
                # 循环遍历获取所有 “电影的对象” movie_obj
                for movie_obj in movie_obj_list:
    
                    # 判断 电影的对象的id == 下载电影记录对象的movie_id
                    if movie_obj.m_id == download_movie_id:
                        download_movie_list.append(
                            [movie_obj.movie_name, download_obj.download_time]
                        )
    
            send_dic = {
                'flag': True,
                'download_movie_list': download_movie_list
            }
    
        print(send_dic)
        common.send_data(send_dic, conn)
    
    
    # 查看公告接口
    @common.login_auth
    def check_notice_interface(back_dic, conn):
        # 1.查看数据库中所有的公告
        notice_obj_list = Notice.select_data()
    
        # 2.判断是否有公告
        if not notice_obj_list:
            send_dic = {
                'flag': False, 'msg': '没有公告!'
            }
    
        else:
            # 公告的标题与内容
            notice_list = []
    
            # 循环遍历所有的公告对象
            for notice_obj in notice_obj_list:
                notice_list.append(
                    [notice_obj.title, notice_obj.content,
                     notice_obj.create_time]
                )
    
            send_dic = {
                'flag': True, 'notice_list': notice_list
            }
    
        common.send_data(send_dic, conn)
    View Code

    lib/common

    import struct
    import json
    import hashlib
    import uuid
    from db.user_data import user_online
    from threading import Lock
    mutex = Lock()
    
    
    # 服务端发送消息给客户端的公共方法
    def send_data(back_dic, conn, file=None):
    
        # 1序列化得到json的bytes数据
        bytes_data = json.dumps(back_dic).encode('utf-8')
    
        # 2.制作报头
        headers = struct.pack('i', len(bytes_data))
    
        # 3.先发送报头
        conn.send(headers)
    
        # 4.再发送真实数据
        conn.send(bytes_data)
    
        if file:
            with open(file, 'rb') as f:
                for line in f:
                    conn.send(line)
    
    
    # 密码加密
    def get_md5(pwd):
        md5_obj = hashlib.md5()
        md5_obj.update(pwd.encode('utf-8'))
        # 加盐
        salt = '啊!!!tank好帅啊,每天被自己帅醒啊!!!'
        md5_obj.update(salt.encode('utf-8'))
        return md5_obj.hexdigest()
    
    
    # 产生随机session字符串
    def get_session():
        uuid_str = str(uuid.uuid4())
        session_val = get_md5(uuid_str)
        return session_val
    
    
    # 登录认证装饰器: 绕过来即可无忧!!!!,绕不过抄10次
    def login_auth(func):
        def inner(*args, **kwargs):  # args = (back_dic, conn)
            # 1.校验当前用户是否登录: 校验session是否一致
            # back_dic.get('cookies')
            client_session = args[0].get('cookies')
            addr = args[0].get('addr')
    
            # ['session2', '用户u_id'] or None
            mutex.acquire()
            session_id_list = user_online.get(addr)
            mutex.release()
    
            if session_id_list:
                if client_session == session_id_list[0]:
                    # 2.若已登录,则将用户id添加到客户端发送过来的字典中
                    # back_dic['user_id'] = session_id_list[1]
                    # 携带user_id去执行接口功能并使用它
                    args[0]['user_id'] = session_id_list[1]
                    res = func(*args, **kwargs)
                    return res
    
                else:
                    send_dic = {
                        'flag': False, 'msg': '携带的session值错误,没有执行权限'
                    }
                    # send_data(send_dic, conn)
                    send_data(send_dic, args[1])
    
            else:
                send_dic = {
                    'flag': False, 'msg': '用户未登录,没有执行权限'
                }
                # send_data(send_dic, conn)
                send_data(send_dic, args[1])
    
        return inner
    View Code

    orm_control/mysql_control

    import pymysql
    from orm_control.mysql_pool import POOL
    
    
    # MySQL连接类
    class MySQL:
    
        __instance = None
    
        # 单例模式
        @classmethod
        def singleton(cls):
            if not cls.__instance:
                cls.__instance = cls()
    
            return cls.__instance
    
        # 实例化MySQL类时,获取数据库链接对象,获取游标对象
        def __init__(self):
            # self.mysql_client = pymysql.connect()
            self.mysql_client = POOL.connection()
    
            self.cursor = self.mysql_client.cursor(
                pymysql.cursors.DictCursor
            )
    
        # 自定义查询方法
        def select(self, sql, args=None):
            # 1、先提交查询sql语句
            # select * from table;
            # select * from table where id=%s;
            self.cursor.execute(sql, args)
    
            # 2、获取返回的查询结果
            # res ---> [{}, {}]
            res = self.cursor.fetchall()
            return res
    
        # 自定义提交sql语句方法,比如: insert、update
        def execute(self, sql, args):
            # 1、提交sql语句
            # insert into table(字段) values(%s);
            try:
                self.cursor.execute(sql, args)
    
            except Exception as e:
                print(e)
    
        def close(self):
            # 先关闭游标
            self.cursor.close()
            # 再关闭数据库连接
            self.mysql_client.close()
    View Code

    orm_control/mysql_pool

    # pip3 install DBUtils
    from DBUtils.PooledDB import PooledDB
    import pymysql
    '''
    数据库连接池
    '''
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxshared=3,
        # 链接池中最多共享的链接数量,0和None表示全部共享。
        # PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
        ping=0,
        # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123456',
        database='orm_demo',
        charset='utf8',
        autocommit=True
    )
    View Code

    orm_control/orm

    '''
    ORM: 对象关系映射
        将对象 映射成 数据表中的一条条记录
    
    类 ----> 表名
    对象 ----> 记录
    对象.属性 ----> 字段
    '''
    
    # 演示映射关系
    '''
    User table: 名字、年龄、性别
        - 名字、年龄、性别
        - jason  80  female
    class User:
    user_obj = User()
    user_obj.name属性
    user_obj.age属性 = 80
    '''
    
    
    from orm_control.mysql_control import MySQL
    
    
    class Field:
        def __init__(self, name,
                     column_type,
                     primary_key,
                     default):
            self.name = name
            self.column_type = column_type
            self.primary_key = primary_key
            self.default = default
    
    
    # varchar
    class StringField(Field):
        def __init__(self, name,
                     column_type='varchar(64)',
                     primary_key=False, default=None):
            super().__init__(name, column_type, primary_key, default)
    
    
    # int
    class IntegerField(Field):
        def __init__(self, name,
                     column_type='int',
                     primary_key=False, default=0):
            super().__init__(name, column_type, primary_key, default)
    
    
    # 自定义元类:
    # 解决三件事情 ---》
    # 1、保证一张表必须要有表名  2、保证一张表中只能有一个主键
    # 3、将所有 “字段名” 与 “字段对象” 添加到一个独立的字典中(mappings),
    # 以key(字段名):value(字段对象) , 添加到类的名称空间中,方便后期使用
    class OrmMetaClass(type):
    
        # __call__ ---> __new__ ----> type.__new__() ---> obj ---> Models
        # 只要定义类就会触发__new__, 因为类也是对象,OrmMetaClass() ---> Models类对象 、User类对象
        def __new__(cls, class_name, class_bases, class_attr):
            # class_name, class_bases, class_attr = args
            # print(f'类名: {class_name}')
            # print(f'基类: {class_bases}')
            # print(f'类的名称空间: {class_attr}')
    
            # 1、过滤Models类
            if class_name == 'Models':
                # 将models类的类名、基类、名称空间原路返回
                return type.__new__(cls, class_name, class_bases, class_attr)
    
            # 2、获取 table 表名,若自定义则获取,没有则默认使用类名
            # dict.get(key) ---> key若有则返回对应的值,若没有则返回默认值 class_name就是默认值
            # 将类名当做表名
            table_name = class_attr.get('table_name', class_name)
    
            # print(table_name)
    
            # 打印修改类名称空间前的  名称空间
            # print(class_attr)
    
            # 主键值: 主键名为 字段名, 比如 主键是 id字段 ---》 id就是主键的名字
            primary_key = None
    
            # 存放字段名与字段对象的字典
            mappings = {}
    
            # 3、保证一张表只能有一个唯一的主键
            # 循环遍历类的名称空间
            for k, v in class_attr.items():
                # print(k, v)
                # 将字段以外的属性过滤掉
                # 判断当前的v是否是字段对象
                if isinstance(v, Field):
                    # print(k, v)
                    # print(v.__dict__)
                    # 4、将所有 “字段名” 与 “字段对象” 添加到一个独立的字典中(mappings)
                    mappings[k] = v
    
                    #
                    # class_attr.pop(k)  # 纠正,这里当字典被迭代时,不能修改其属性
    
                    # 判断字段对象如果有 主键primary_key, 则为primary_key 变量赋值
                    if v.primary_key:
                        # 若第二次进来,primary有值,证明有主键,抛出异常
                        if primary_key:
                            raise TypeError('一张表只能有一个主键')
    
                        # primary_key = k
                        # print(k == v.name, 111111)
                        # 给primary_key变量做一个赋值操作
                        primary_key = v.name
    
            # 5、过滤掉类名称空间中重复的字段属性
            for key in mappings.keys():
                class_attr.pop(key)
    
    
            # print(table_name)
            # print(primary_key)
            # print(mappings)
            # print(class_attr)
    
            if not primary_key:
                raise TypeError('必须要有一个主键!!!')
    
            # 6、给类的名称空间,添加table_name, primary_key,mappings属性;
            class_attr['table_name'] = table_name  # cls.table_name
            class_attr['primary_key'] = primary_key
            class_attr['mappings'] = mappings
    
            # print('*' * 100)
            # print(class_attr)
    
            return type.__new__(cls, class_name, class_bases, class_attr)
    
    
    class Models(dict, metaclass=OrmMetaClass):
    
        # 对象.属性, 属性没有时触发
        def __getattr__(self, item):
            # print(self)
            return self.get(item)  # {'name': 'tank', 'pwd': '123'}.get('name') ----> tank
    
        # 对象.属性 = 属性值 时触发
        def __setattr__(self, key, value):
            # print(key, value)
            # print(self)  # {'name': 'tank', 'pwd': '123'}
            # 给字典添加键值对的方式
            self[key] = value
    
        # 查询数据
        @classmethod
        def select_data(cls, **kwargs):  # name=tank ---> {'name': "tank"}
            mysql_obj = MySQL()
    
            filed_value = None
    
            # 若kwargs为False代表没有查询条件
            if not kwargs:
                # 1、查所有
                # sql = 'select * from 表名'
                sql = f'select * from {cls.table_name}'
    
            else:
                # 獲取字段名
                filed_name = list(kwargs.keys())[0]
    
                # 获取字段值
                filed_value = kwargs.get(filed_name)
    
                # 2、根据条件查询
                # select * from 表名 where 字段名=字段值;
                sql = f'select * from {cls.table_name} where {filed_name}=?'
                sql = sql.replace('?', '%s')
    
            res = mysql_obj.select(sql, filed_value)
    
            # print(res)  # [{}, {}]
            # [cls(**r) for r in res]  # [{key: values}, {}]  ---> [User(key=value)]
            return [cls(**r) for r in res]  # ----> [{}, {}, {}] ----> [obj, obj, obj]
    
        # 插入数据
        def insert_data(self):  # self ---> user_obj
            mysql_obj = MySQL()
    
            # sql: insert into 表名(字段名1, 字段名2) values(字段值1, 字段值2);
            # 1.表名  ---》 self.table_name
            # 2.字段名与字段值---》 mappings
    
            # 存放字段名的列表
            filed_names = []  # [字段名, 字段名, 字段名, ]
    
            # 存放字段值的列表
            filed_values = []  # [字段值, 字段值,字段值,]
    
            # 设置一个替换值的列表
            replace_list = []  # [?, ?]
    
            for k, v in self.mappings.items():
    
                # 获取字段名,追加到列表中
                # filed_names.append(k)
                filed_names.append(v.name)
    
                # User(username='tank', password='123')
                # 获取字段值,追加到列表中
                # getattr反射返回的值是__getattr__返回的结果
                # res = getattr(self, v.name, v.default)  # v.default ---> 0
    
                res = self.get(v.name, v.default)
    
                # res = getattr(self, v.name) if getattr(self, v.name) else v.default
    
                print(f'{v.name}', res)
                filed_values.append(
                    # 反射: 根据字符串操作对象中的属性或方法
                    res
                )
                replace_list.append('?')
    
            # sql = f'insert into {self.table_name}({",".join(filed_names)}) values({",".join(replace_list)})'
            sql = 'insert into %s(%s) values(%s)' % (
                self.table_name, ",".join(filed_names), ",".join(replace_list)
            )
            sql = sql.replace('?', '%s')
    
            mysql_obj.execute(sql, filed_values)
    
        # 更新数据
        def update_data(self):
            mysql_obj = MySQL()
            # sql: update 表名 set 字段名=字段值 where pk=主键值;
    
            # 主键值
            pk = None
    
            # 存放字段名的列表
            filed_names = []  # [字段名, 字段名, 字段名, ]
    
            # 存放字段值的列表
            filed_values = []  # [字段值, 字段值,字段值,]
    
            for k, v in self.mappings.items():
                # 判断mappings中哪一个字段是主键
                if v.primary_key:
                    # 获取主键的值
                    pk = self.get(v.name)
    
                else:
                    # 添加字段名
                    filed_names.append(v.name + '=?')
    
                    # 添加字段值
                    filed_values.append(
                        self.get(v.name, v.default)
                    )
    
            # sql: update User set 字段名=字段值, 字段名=字段值
            # sql: update User set username=?, password=? where pk=1;
            sql = 'update %s set %s where %s=%s' %(
                self.table_name,
                ','.join(filed_names),
                self.primary_key,
                pk
            )
            # sql: update User set username=%s, password=%s where pk=1;
            sql = sql.replace('?', '%s')
    
            mysql_obj.execute(sql, filed_values)
    View Code

    tcp_server/socket_server

    import socket
    import struct
    import json
    from interface import admin_interface
    from interface import common_interface
    from interface import user_interface
    from concurrent.futures import ThreadPoolExecutor
    from db import user_data
    from threading import Lock
    pool = ThreadPoolExecutor(50)
    mutex = Lock()
    
    
    # 接口字典
    func_dic = {
        'register': common_interface.register_interface,
        'login': common_interface.login_interface,
    
    
        # 检测电影接口
        'check_movie': admin_interface.check_movie_interface,
    
        # 上传电影功能接口
        'upload_movie': admin_interface.upload_movie_interface,
    
        # 获取未删除电影接口
        'get_movie_list': common_interface.get_movie_list_interface,
    
        # 删除电影接口
        'delete_movie': admin_interface.delete_movie_interface,
    
        # 发布公告接口
        'send_notice': admin_interface.send_notice_interface,
    
        # 购买会员接口
        'buy_vip': user_interface.buy_vip_interface,
    
        # 下载电影接口
        'download_movie': common_interface.download_free_movie_interface,
    
        # 查看下载记录接口
        'check_movie_record': user_interface.check_movie_record_interface,
    
        # 查看公告接口
        'check_notice': user_interface.check_notice_interface,
    }
    
    
    # 任务函数
    def working(conn, addr):
        while True:
            try:
                # 1.先接收报头
                headers = conn.recv(4)
                # 2.解压报头,获取真实数据的长度
                # struct.unpack得到一个元组
                bytes_len = struct.unpack('i', headers)[0]
                # 3.再接收真实数据
                json_bytes_data = conn.recv(bytes_len).decode('utf-8')
                # 4.反序列化得到真实数据字典
                back_dic = json.loads(json_bytes_data)
    
                # 保证每个客户端过来都是唯一,将addr添加到客户端发送过来的字典中
                back_dic['addr'] = str(addr)
    
                func_type = back_dic.get('func_type')
                if func_type in func_dic:
                    func_dic.get(func_type)(back_dic, conn)
    
            except Exception as e:
                print(e)
                mutex.acquire()
                # 清除当前客户端存放在服务端的session值
                user_data.user_online.pop(str(addr))
                mutex.release()
                break
    
        conn.close()
    
    
    # 实现并发
    def run():
        server = socket.socket()
        server.bind(
            ('127.0.0.1', 9527)
        )
        server.listen(50)
    
        while True:
            conn, addr = server.accept()
            print(addr)
            # 异步提交working任务
            pool.submit(working, conn, addr)
    View Code

    start

    import sys
    import os
    sys.path.append(
        os.path.dirname(__file__)
    )
    
    from tcp_server import socket_server
    
    if __name__ == '__main__':
        print('Server is running...')
        socket_server.run()
    View Code
  • 相关阅读:
    理解Docker(6):若干企业生产环境中的容器网络方案
    理解Docker(5):Docker 网络
    理解Docker(4):Docker 容器使用 cgroups 限制资源使用
    理解Docker(3):Docker 使用 Linux namespace 隔离容器的运行环境
    理解Docker(2):Docker 镜像
    理解Docker(1):Docker 安装和基础用法
    OpenStack 行业正进入拓展期:行业云将成为新一轮工业革命的基础设施和引擎
    PHP通用返回值设置
    C++ 模板学习 函数模板、类模板、迭代器模板
    C++/Php/Python 语言执行shell命令
  • 原文地址:https://www.cnblogs.com/ludingchao/p/12093546.html
Copyright © 2011-2022 走看看