优酷系统

涵盖内容:

线程池,锁机制,session验证机制,简易版orm,大文件md5校验,数据库操作

服务端目录

conf

import os

BASE_PATH = os.path.dirname(
    os.path.dirname(__file__)
)

DOWNLOAD_PATH = os.path.join(
    BASE_PATH, 'download_files')
settings

db

from orm.orm import Models, StringField, IntegerField


# 用户表
class User(Models):
    # 表名
    table_name = 'user'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    name = StringField(name='name')
    #  pwd, is_vip, is_locked, user_type, register_time
    pwd = StringField(name='pwd')
    is_vip = IntegerField(name='is_vip')
    is_locked = IntegerField(name='is_locked')
    user_type = StringField(name='user_type')
    register_time = StringField(name='register_time')


# 电影表
class Movie(Models):
    # 表名
    table_name = 'movie'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    name = StringField(name='name')
    # path, is_free, file_md5, user_id, is_delete, upload_time
    path = StringField(name='path')
    is_free = IntegerField(name='is_free')  # 1   0
    file_md5 = StringField(name='file_md5')
    user_id = IntegerField(name='user_id')
    is_delete = IntegerField(name='is_delete')
    upload_time = StringField(name='upload_time')


# 公告表
class Notice(Models):
    table_name = 'notice'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    title = StringField(name='title')
    content = StringField(name='content')
    user_id = IntegerField(name='user_id')
    create_time = StringField(name='create_time')


# 下载记录表
class DownloadRecord(Models):
    table_name = 'download_record'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    user_id = IntegerField(name='user_id')
    movie_id = IntegerField(name='movie_id')
    download_time = StringField(name='download_time')
models.py
user_online = {
    # addr: [session, user_id]
}
user_data

download_files

文本文件,添加下载电影

interface

from lib import common
from db import models
import os
from conf import settings

@common.login_auth
def upload_movie_interface(client_back_dic, conn):
    print('炮王来交货啦!')

    # 确保电影名称是唯一的  随机字符串 + 电影名称
    movie_name = common.get_random_code() + client_back_dic.get('movie_name')  # .mp4

    movie_size = client_back_dic.get('file_size')

    movie_path = os.path.join(
        settings.DOWNLOAD_PATH, movie_name
    )

    # 1.接受上传的电影
    data_recv = 0
    with open(movie_path, 'wb') as f:
        while data_recv < movie_size:
            data = conn.recv(1024)
            f.write(data)
            data_recv += len(data)

    # 2.把电影数据保存到mysql中
    movie_obj = models.Movie(
        name=movie_name, file_md5=client_back_dic.get('file_md5'),
        is_free=client_back_dic.get('is_free'), is_delete=0,
        path=movie_path, user_id=client_back_dic.get('user_id'),
        upload_time=common.get_time()
    )
    movie_obj.save()

    send_dic = {
        'flag': True, 'msg': f'{client_back_dic.get("movie_name")}电影上传成功!'
    }

    common.send_data(send_dic, conn)


@common.login_auth
def check_movie_interface(client_back_dic, conn):

    file_md5 = client_back_dic.get('file_md5')

    movie_list = models.Movie.select(file_md5=file_md5)

    if movie_list:

        send_dic = {
            'flag': False, 'msg': '电影已存在!'
        }

    else:

        send_dic = {
            'flag': True, 'msg': '电影可以上传'
        }

    common.send_data(send_dic, conn)


@common.login_auth
def delete_movie_interface(client_back_dic, conn):
    movie_id = client_back_dic.get('movie_id')
    # 直接删除
    movie_obj = models.Movie.select(id=movie_id)[0]
    movie_obj.is_delete = 1
    # 调用更新方法
    movie_obj.sql_update()

    send_dic = {
        'flag': True, 'msg': '电影删除成功!'
    }

    common.send_data(send_dic, conn)


@common.login_auth
def put_notice_interface(client_back_dic, conn):
    title = client_back_dic.get('title')
    content = client_back_dic.get('content')
    user_id = client_back_dic.get('user_id')
    notice_obj = models.Notice(title=title, content=content, user_id=user_id,
                  create_time=common.get_time())

    notice_obj.save()

    send_dic = {
        'msg': '公告发布成功!'
    }

    common.send_data(send_dic, conn)
admin_interface
from db import models
from lib import common, lock_file
from db import user_data


def register_interface(client_back_dic, conn):

    # 写业务逻辑
    # 1.判断用户名是否存在
    username = client_back_dic.get('username')
    # 通过用户名当作条件查询
    user_obj_list = models.User.select(name=username)

    # 若存在,给客户端返回数据, 告诉用户,用户已存在!
    if user_obj_list:
        send_dic = {'flag': False, 'msg': '用户已存在!'}

    # 若不存在,保存数据到MySQL数据库中, 返回注册成功给客户端
    else:
        password = client_back_dic.get('password')
        user_obj = models.User(
            name=username,
            #  pwd, is_vip, is_locked, user_type, register_time
            pwd=common.get_md5_pwd(password),
            is_vip=0,  # 0表示不是VIP, 1表示VIP
            is_locked=0,  # 0表示不锁定, 1表示锁定
            user_type=client_back_dic.get('user_type'),
            register_time=common.get_time())

        user_obj.save()

        send_dic = {'flag': True, 'msg': '注册成功'}

    common.send_data(send_dic, conn)


def login_interface(client_back_dic, conn):
    # 执行login业务逻辑
    username = client_back_dic.get('username')

    # 1.根据用户名查询用户数据
    user_list = models.User.select(name=username)

    if not user_list:
        send_dic = {'flag': False, 'msg': '用户不存在'}

    else:
        user_obj = user_list[0]
        password = client_back_dic.get('password')
        # 1.判断客户端传入的密码与数据库中的密码是否相等
        if user_obj.pwd == common.get_md5_pwd(password):

            # 产生一个随机字符串,作为session值
            session = common.get_random_code()
            addr = client_back_dic.get('addr')
            # 保存session值到服务端,session + user_id一同保存到服务端本地
            # 使用锁写入数据
            lock_file.mutex.acquire()
            #  str(addr)
            user_data.user_online[addr] = [session, user_obj.id]
            lock_file.mutex.release()

            send_dic = {'flag': True, 'msg': '登录成功!',
                       'session': session, 'is_vip': user_obj.is_vip, 'new_notice': None}

            new_notice = get_new_notice_interface()

            if not new_notice:
                pass

            else:
                send_dic['new_notice'] = new_notice

        else:
            send_dic = {'flag': False, 'msg': '密码错误!'}

    common.send_data(send_dic, conn)


# 获取电影接口
@common.login_auth
def get_movie_list_interface(client_back_dic, conn):

    # 获取所有电影对象
    movie_obj_list = models.Movie.select()
    # 添加未删除的电影列表
    back_movie_list = []

    if movie_obj_list:

        # 过滤已删除的电影
        for movie_obj in movie_obj_list:

            # 没有删除则返回
            if not movie_obj.is_delete:  # 1 代表删除

                if client_back_dic.get('movie_type') == 'all':
                    back_movie_list.append(
                        # [电影名称、是否免费、电影ID]
                        [movie_obj.name,'免费' if movie_obj.is_free else "收费", movie_obj.id]
                    )

                elif client_back_dic.get('movie_type') == 'free':
                    # 判断哪些电影是免费的
                    if movie_obj.is_free:
                        back_movie_list.append(
                            [movie_obj.name, '免费', movie_obj.id]
                        )

                else:
                    if not movie_obj.is_free:
                        back_movie_list.append(
                            [movie_obj.name, '收费', movie_obj.id]
                        )



        if back_movie_list:

            send_dic = {'flag': True, 'back_movie_list': back_movie_list}

        else:
            send_dic = {'flag': False, 'msg': '没有电影!'}
    else:

        send_dic = {'flag': False, 'msg': '没有电影!'}

    common.send_data(send_dic, conn)



def get_new_notice_interface():

    # 1.获取所有的公告
    notice_obj_list = models.Notice.select()  # 展示此处notice_obj_list的数据
    if not notice_obj_list:
        return False

    # 2.对公告的发布时间或者id进行排序,获取最新的一条公告

    notice_desc_list = sorted(
        # [notice_obj, notice_obj,notice_obj。。。]
        # 选择1:根据ID notice_obj_list, key=lambda notice_obj: notice_obj.id
        # 选择2:根据时间
        notice_obj_list, key=lambda notice_obj: notice_obj.create_time, reverse=True
    )

    new_notice = {
        'title': notice_desc_list[0].title,
        'content': notice_desc_list[0].content,
    }
    return new_notice
common_interface
from db import models
from lib import common
import os
from conf import settings


@common.login_auth
def buy_vip_interface(client_back_dic, conn):

    user_id = client_back_dic.get('user_id')

    user_obj = models.User.select(id=user_id)[0]
    user_obj.is_vip = 1
    user_obj.sql_update()

    send_dic = {'flag': True, 'msg': '会员充值成功!'}

    common.send_data(send_dic, conn)


@common.login_auth
def download_movie_interface(client_back_dic, conn):

    movie_id = client_back_dic.get('movie_id')
    movie_name = client_back_dic.get('movie_name')
    movie_type = client_back_dic.get('movie_type')
    user_id = client_back_dic.get('user_id')
    # movie_obj = models.Movie.select(id=movie_id)[0]
    # movie_path = movie_obj.path
    movie_path = os.path.join(settings.DOWNLOAD_PATH, movie_name)
    movie_size = os.path.getsize(movie_path)
    send_dic = {
        'flag': True, 'msg': '准备下载', 'movie_size': movie_size
    }
    user_obj = models.User.select(id=user_id)[0]

    if movie_type == '免费':
        wait_time = 0

        if not user_obj.is_vip:

            wait_time = 20

        send_dic['wait_time'] = wait_time

    print(send_dic)
    common.send_data(send_dic, conn, movie_path)

    obj = models.DownloadRecord(user_id=user_id, movie_id=movie_id,
                          download_time=common.get_time())
    obj.save()


@common.login_auth
def check_download_record_interface(client_back_dic, conn):
    record_obj_list = models.DownloadRecord.select()
    user_id = client_back_dic.get('user_id')

    back_record_list = []

    if record_obj_list:
        for record_obj in record_obj_list:
            if record_obj.user_id == user_id:

                # 获取当前用户下载电影记录的电影对象
                movie_obj = models.Movie.select(id=record_obj.movie_id)[0]

                back_record_list.append(
                    movie_obj.name
                )

            else:
                send_dic = {'flag': False, 'msg': '当前用户没有下载记录!'}

        send_dic = {
            'flag':True, 'record_list': back_record_list
        }

    else:
        send_dic = {'flag': False, 'msg': '没有任何下载记录!'}

    common.send_data(send_dic, conn)


@common.login_auth
def check_all_notice_interface(client_back_dic, conn):

    notice_obj_list = models.Notice.select()

    back_notice_list = []

    if notice_obj_list:

        for notice_obj in notice_obj_list:
            title = notice_obj.title
            content = notice_obj.content
            back_notice_list.append(
                {'title': title, 'content': content}
            )

        send_dic = {'flag': True, 'back_notice_list': back_notice_list}

    else:
        send_dic = {'flag': False, 'msg': '没有公告!'}

    common.send_data(send_dic, conn)
user_interface

lib

import time
import hashlib
import json
import struct
import uuid
from functools import wraps
from db import user_data


def get_time():

    now_time = time.strftime('%Y-%m-%d %X')

    return now_time


def get_md5_pwd(pwd):
    md = hashlib.md5()

    md.update(pwd.encode('utf-8'))
    md.update('虹桥炮王,Jason是也!'.encode('utf-8'))

    return md.hexdigest()



def send_data(send_dic, conn, file=None):
    data_bytes = json.dumps(send_dic).encode('utf-8')
    headers = struct.pack('i', len(data_bytes))
    conn.send(headers)
    conn.send(data_bytes)
    if file:
        with open(file, 'rb') as f:
            for line in f:
                conn.send(line)


def get_random_code():
    # uuid可以产生一个世界上唯一的字符串
    md5 = hashlib.md5()
    md5.update(str(uuid.uuid4()).encode('utf-8'))
    return md5.hexdigest()


# 登录认证装饰器
def login_auth(func):
    @wraps(func)
    # (client_back_dic, conn) = args
    def inner(*args, **kwargs):
        # if args[0].get('session') == 服务端存放的session值:
        # # [session, user_id] = values

        addr = args[0].get('addr')
        # addr: [session, user_id]

        # [session, user_id]
        user_session = user_data.user_online.get(addr)
        if user_session:
            if args[0].get('session') == user_session[0]:

                args[0]['user_id'] = user_session[1]
        #
        # for values in user_data.user_online.values():
        #     if args[0].get('session') == values[0]:
        #         # 添加到client_back_dic
        #         args[0]['user_id'] = values[1]  # user_id

        # 判断user_id是否存在
        if args[0].get('user_id'):
            func(*args, **kwargs)

        else:
            send_dic = {'flag': False, 'msg': '未登录,请去登录!'}
            # send_data(send_dic, conn)

            send_data(send_dic, args[1])

    return inner
common.py
mutex = None
lock_file.py

orm

'''
定义字段类
'''
from orm.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(255)', 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)


# 元类控制表模型类的创建
class OrmMetaClass(type):

    # 类名, 类的基类, 类的名称空间
    def __new__(cls, class_name, class_bases, class_attr):
        # print(class_name, class_bases, class_attr)
        # 1.过滤Models类
        if class_name == 'Models':
            return type.__new__(cls, class_name, class_bases, class_attr)

        # 2.控制模型表中: 表名, 主键, 表的字段
        # 如果模型表类中没有定义table_name,把类名当做表名

        # 获取表名
        table_name = class_attr.get('table_name', class_name)  # user_info, User

        # 3.判断是否只有一个主键
        primary_key = None

        # 用来存放所有的表字段, 存不是目的,目的是为了取值方便
        mappings = {}

        '''
        __main__: xxxx
        'id': <__main__.IntegerField object at 0x000001E067D48B00>, 
        'name': <__main__.StringField object at 0x000001E067D48AC8>}
        '''
        for key, value in class_attr.items():

            # 判断value是否是字段类的对象
            if isinstance(value, Field):

                # 把所有字段都添加到mappings中
                mappings[key] = value

                if value.primary_key:

                    if primary_key:
                        raise TypeError('主键只能有一个')

                    # 获取主键
                    primary_key = value.name

        # 删除class_attr中与mappings重复的属性, 节省资源
        for key in mappings.keys():
            class_attr.pop(key)

        # 判断是否有主键
        if not primary_key:
            raise TypeError('必须要有一个主键')

        class_attr['table_name'] = table_name
        class_attr['primary_key'] = primary_key
        class_attr['mappings'] = mappings
        '''
               'table_name': table_name
               'primary_key': primary_key
               'mappings': {'id': <__main__.IntegerField object at 0x000001E067D48B00>,
                            'name': <__main__.StringField object at 0x000001E067D48AC8>}
                            }
       '''
        return type.__new__(cls, class_name, class_bases, class_attr)


# 继承字典类,
class Models(dict, metaclass=OrmMetaClass):
    def __init__(self, **kwargs):
        # print(kwargs)  # 接收关键字参数
        super().__init__(**kwargs)

    # 在对象.属性没有的时候触发
    def __getattr__(self, item):
        # print(item)
        return self.get(item, '没有这个key')

    # 在对象.属性 = 属性值 时触发
    def __setattr__(self, key, value):

        # 字典赋值操作
        self[key] = value

    #
    @classmethod
    def select(cls, **kwargs):

        # 获取数据库链接对象
        ms = Mysql()

        # 若没有kwargs代表没有条件查询
        if not kwargs:
            # select * from table;
            sql = 'select * from %s' % cls.table_name

            res = ms.my_select(sql)

        # 若有kwargs代表有条件
        else:
            # print(kwargs)  # {id:1}
            key = list(kwargs.keys())[0]  # id
            value = kwargs.get(key)  # 1

            # select * from table where id=1;
            sql = 'select * from %s where %s=?' % (
                cls.table_name, key
            )

            sql = sql.replace('?', '%s')

            res = ms.my_select(sql, value)

        if res:
            # [{},{}, {}]   ---->  [obj1, obj2, obj3]
            # 把mysql返回来的 列表套 字典 ---> 列表套 对象
            # l1 = []
            # # 遍历mysql返回所有的字典
            # for d in res:
            #     # 把每一个字典传给cls实例化成一个个的r1对象
            #     r1 = cls(**d)
            #     # 追加到l1列表中
            #     l1.append(r1)

            return [cls(**result) for result in res]

    # 插入
    def save(self):
        ms = Mysql()
        # insert into table(x,x,x) values(x,x,x);

        # 字段名
        fields = []
        # 字段的值
        values = []
        # 存放对应字段的?号
        args = []

        for k, v in self.mappings.items():
            # 把主键过滤掉
            if not v.primary_key:
                fields.append(
                    v.name
                )
                values.append(
                    getattr(self, v.name, v.default)
                )
                args.append('?')

        # insert into table(x,x,x) values(?, ?, ?);
        sql = 'insert into %s(%s) values(%s)' % (
            self.table_name, ','.join(fields), ','.join(args)
        )

        sql = sql.replace('?', '%s')

        ms.my_execute(sql, values)

    # 更新
    def sql_update(self):。
        ms = Mysql()

        fields = []
        primary_key = None
        values = []

        for k, v in self.mappings.items():
            # 获取主键的值
            if v.primary_key:
                primary_key = getattr(self, v.name, v.default)

            else:

                # 获取 字段名=?, 字段名=?,字段名=?
                fields.append(
                    v.name + '=?'
                )

                # 获取所有字段的值
                values.append(
                    getattr(self, v.name, v.default)
                )

        # update table set %s=?,... where id=1;  把主键当做where条件
        sql = 'update %s set %s where %s=%s' % (
            self.table_name, ','.join(fields), self.primary_key, primary_key
        )

        # print(sql)  # update User set name=? where id=3

        sql = sql.replace('?', '%s')

        ms.my_execute(sql, values)


# User, Movie, Notice
# 表模型类
class User(Models):
    # table_name = 'user_info'
    id = IntegerField(name='id', primary_key=True)
    name = StringField(name='name')
    # pwd = StringField(name='pwd')


class Movie(Models):
    id = IntegerField(name='id', primary_key=True)
    pass


# # # User('出入任意个数的关键字参数')
# user_obj = User()  # user_obj--->dict
# user_obj.name = 'xxxx'

# if __name__ == '__main__':
#     res = User.select(name='jason_sb')[0]
#     print(res)
#
#     # res.name = 'jason_sb'
#     #
#     # res.sql_update()
#
#     # user_obj = User(name='egon')
#     # user_obj.save()
#
# '''
# 表:
#     表名, 只有一个唯一的主键, 字段(必须是Field的字段)
#
# 元类:
#     通过元类控制类的创建.
# '''
#
# # class Movie:
# #     def __init__(self, movie_name, movie_type):
# #         self.movie_name = movie_name
# #         self.movie_type = movie_type
# #
# #
# # class Notice:
# #     def __init__(self, title, content):
# #         self.title = title
# #         self.content = content
#
# '''
# 问题1: 所有表类都要写__init__, 继承一个父类
# 问题2: 可以接收任意个数以及任意名字的关键字参数. 继承python中的字典对象.
# '''
#
# # if __name__ == '__main__':
# #     # d1 = dict({'name': 'tank'})
# #     # d2 = dict(name='tank2')
# #     # print(d1)
# #     # print(d2)
# #
# #     d3 = Models(name='jason')
# #     # print(d3)
# #     # print(d3.get('name'))
# #     # print(d3['name'])
# #     # print(d3.name)
# #     # d3.name = 'tank'
# #     # d3.pwd = '123'
# #     # print(d3.name)
# #     # print(d3)
# #     print(d3.name)  # None
# #
# #     d3.pwd = '123'
# #     print(d3.pwd)
# #     print(d3)


if __name__ == '__main__':
    # 查看所有
    # res = User.select()
    # print(res)

    # 根据查询条件查询
    res = User.select(name='json_egon_sb')
    print(res)
#
#     # 更新
#     # user_obj = res[0]
#     # user_obj.name = 'jason_sb_sb'
#     # user_obj.sql_update()  # {'id': 3, 'name': 'jason_sb'}
#
#     # 插入
#     # user_obj = User(name='json_egon_sb')
# #     # user_obj.save()
orm.py
import pymysql
from orm.db_pool import POOL


class Mysql:

    def __init__(self):
        # 建立链接
        self.conn = POOL.connection()

        # 获取游标
        self.cursor = self.conn.cursor(
            pymysql.cursors.DictCursor
        )

    # 关闭游标\链接方法
    def close_db(self):
        self.cursor.close()
        self.conn.close()

    # 查看
    def my_select(self, sql, args=None):

        self.cursor.execute(sql, args)

        res = self.cursor.fetchall()
        # [{}, {}, {}]
        # print(res)
        return res

    # 提交
    def my_execute(self, sql, args):
        try:
            # 把insert , update...一系列sql提交到mysql中
            self.cursor.execute(sql, args)

        except Exception as e:
            print(e)
mysql_control.py
from DBUtils.PooledDB import PooledDB
import pymysql
# pip3 install DBUtils

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='qinsungui',
    database='youku_demo11',
    charset='utf8',
    autocommit='True'
)
db_pool.py

tcp_server

import socket
import struct
import json
from interface import common_interface
from interface import admin_interface
from interface import user_interface
from concurrent.futures import ThreadPoolExecutor
from threading import Lock
from lib import lock_file
from db import user_data

lock = Lock()

lock_file.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,

    'put_notice': admin_interface.put_notice_interface,

    # 普通用户功能
    'buy_vip': user_interface.buy_vip_interface,
    'download_movie': user_interface.download_movie_interface,
    'check_download_record': user_interface.check_download_record_interface,
    'check_all_notice': user_interface.check_all_notice_interface,
}


class SocketServer:
    def __init__(self):
        self.server = socket.socket()
        self.server.bind(
            ('127.0.0.1', 9527)
        )
        self.server.listen(5)
        self.pool = ThreadPoolExecutor(50)


    def run(self):
        print('启动服务端...')
        while True:
            conn, addr = self.server.accept()  # 1 2 3
            # 通过self.pool 线程池异步提交任务(working)
            # self.pool.submit(函数对象, 传递给函数对象的参数1, 参数2)  # 1 2 3
            self.pool.submit(self.working, conn, addr)  # 1 2 3

    # 任务分发
    def dispatcher(self, client_back_dic, conn):
        # # 判断功能的类型
        # if client_back_dic.get('type') == 'register':
        #     common_interface.register_interface(client_back_dic, conn)
        #
        # elif client_back_dic.get('type') == 'login':
        #     common_interface.login_interface(client_back_dic, conn)

        # 通过_type变量接受客户端选择的功能类型
        _type = client_back_dic.get('type')  # login

        if _type in func_dic:  # register
            func_dic.get(_type)(client_back_dic, conn)

    # 用于执行客户端连接任务
    def working(self,  conn, addr):
        while True:
            try:
                # 每一个客户端访问服务端都会经过此处
                # 此处用于接收客户端传入的数据
                headers = conn.recv(4)
                data_len = struct.unpack('i', headers)[0]
                data_bytes = conn.recv(data_len)
                client_back_dic = json.loads(data_bytes.decode('utf-8'))
                # 把每个用户的addr一并赋值给客户端传过来的字典
                client_back_dic['addr'] = str(addr)
                # 把客户端传递过来的字典与conn传给dispatcher执行
                self.dispatcher(client_back_dic, conn)

            except Exception as e:
                print(e)
                lock.acquire()
                user_data.user_online.pop(str(addr))
                lock.release()
                conn.close()
                break
socket_server

readme

start.py

import os
import sys
sys.path.append(
    os.path.dirname(__file__)
)
from tcp_server.socket_server import SocketServer


if __name__ == '__main__':
    server = SocketServer()  # __init

    server.run()  #
start.py

客户端目录

conf

import os

BASE_PATH = os.path.dirname(
    os.path.dirname(__file__))

UPLOAD_FILES = os.path.join(
    BASE_PATH, 'upload_files')

DOWNLOAD_FILES = os.path.join(
    BASE_PATH, 'download_files')
settings.py

core

from tcp_client import socket_client
from lib import common
import os
from conf import settings
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,
                        'type': 'register',
                        'user_type': 'admin'}
            # {'flag': False, 'msg': '用户已存在!'}
            # {'flag': True, 'msg': '注册成功'}
            back_dic = common.send_msg_back_dic(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()

        send_dic = {
            'type': 'login',  # 判断对应的接口
            'username': username,
            'password': password,
            'user_type': 'admin'
        }
        # {'flag': False, 'msg': '用户不存在'}
        back_dic = common.send_msg_back_dic(send_dic, client)

        if back_dic.get('flag'):
            session = back_dic.get('session')
            user_info['cookies'] = session
            print(back_dic.get('msg'))
            break

        else:
            print(back_dic.get('msg'))

# 上传电影
def upload_movie(client):
    while True:
        # 1.打印电影列表
        movie_list = common.get_movie_list()
        for index, movie in enumerate(movie_list):
            print(index, movie)

        choice = input('请输入上传的电影编号:').strip()

        if not choice.isdigit():
            print('请输入数字!')
            continue

        choice = int(choice)

        if choice not in range(len(movie_list)):
            print("请选择正确编号!")
            continue

        # 用户选择电影
        movie_name = movie_list[choice]

        # 上传电影绝对路径
        movie_path = os.path.join(
            settings.UPLOAD_FILES, movie_name
        )

        # 2.去服务端校验电影是否存在
        file_md5 = common.get_movie_md5(movie_path)

        send_dic = {
            'type': 'check_movie',
            'session': user_info.get('cookies'),
            'file_md5': file_md5
        }

        back_dic = common.send_msg_back_dic(
            send_dic, client)

        if back_dic.get('flag'):
            # 电影可以上传
            print(back_dic.get('msg'))

            # 上传电影功能字典
            send_dic = {
                'type': 'upload_movie',
                'file_md5': file_md5,
                # 大小用来判断服务端需要接受文件的大小
                'file_size': os.path.getsize(movie_path),
                'movie_name': movie_name,
                'session': user_info.get('cookies')
            }

            is_free = input('上传电影是否免费: y/n').strip()

            if is_free == 'y':
                send_dic['is_free'] = 1

            else:
                send_dic['is_free'] = 0

            back_dic = common.send_msg_back_dic(
                send_dic, client, file=movie_path)

            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break

        else:
            print(back_dic.get('msg'))



    #
    #
    # send_dic = {'type': 'upload_movie','session': user_info.get('cookies')}
    # back_dic = common.send_msg_back_dic(send_dic, client)
    # print(back_dic)

# 删除电影
def delete_movie(client):
    while True:
        # 1.从服务端获取电影列表
        send_dic = {'type': 'get_movie_list',
                    'session': user_info.get('cookies')}

        # 发送获取电影请求
        back_dic = common.send_msg_back_dic(
            send_dic, client)

        if back_dic.get('flag'):
            back_movie_list = back_dic.get('back_movie_list')
            # 打印选择的电影
            for index, movie_list in enumerate(back_movie_list):
                print(index, movie_list)

            # 2.选择需要删除的电影
            choice = input('请输入需要删除的电影编号:').strip()

            if not choice.isdigit():
                continue

            choice = int(choice)

            if choice not in range(len(back_movie_list)):
                continue

            # 获取电影ID,传递给服务端,让服务端去mysql数据库修改当前电影对象的is_delete=1
            movie_id = back_movie_list[choice][2]

            send_dic = {
                'type': 'delete_movie', 'movie_id': movie_id,
                'session': user_info.get('cookies')
            }

            # 发送删除电影请求
            back_dic = common.send_msg_back_dic(send_dic, client)

            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
        else:
            print(back_dic.get('msg'))
            break

# 发布公告
def put_notice(client):
    title = input('请输入公告标题:').strip()
    content = input('请输入公告内容:').strip()

    send_dic = {
        'type': 'put_notice',
        'session': user_info.get('cookies'),
        'title': title,
        'content': content
    }
    back_dic = common.send_msg_back_dic(send_dic, client)

    print(back_dic.get('msg'))

func_dic = {
    '1': register,
    '2': login,
    '3': upload_movie,
    '4': delete_movie,
    '5': put_notice,
}

def admin_view():

    sk_client = socket_client.SocketClient()
    client = sk_client.get_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)
admin.py
from core import admin, user


func_dic = {

    '1': admin.admin_view,
    '2': user.user_view,
}
def run():
    while True:
        print('''
        1.管理员功能
        2.用户功能
        q.退出
        ''')

        choice = input('请选择功能编号: ').strip()

        if choice == 'q':
            break

        if choice not in func_dic:
            continue

        func_dic.get(choice)()
src.py
from tcp_client.socket_client import SocketClient
from lib import common
from conf import settings
import time
import os
user_info = {
    'cookies': None,
    'is_vip': 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,
                        'type': 'register',
                        'user_type': 'user'}
            # {'flag': False, 'msg': '用户已存在!'}
            # {'flag': True, 'msg': '注册成功'}
            back_dic = common.send_msg_back_dic(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()

        send_dic = {
            'type': 'login',  # 判断对应的接口
            'username': username,
            'password': password,
            'user_type': 'user'
        }
        # {'flag': False, 'msg': '用户不存在'}
        back_dic = common.send_msg_back_dic(send_dic, client)

        if back_dic.get('flag'):
            session = back_dic.get('session')
            user_info['cookies'] = session
            user_info['is_vip'] = back_dic.get('is_vip')
            print(back_dic.get('msg'))

            # 打印最新公告
            if back_dic.get('new_notice'):
                print(back_dic.get('new_notice'))

            break

        else:
            print(back_dic.get('msg'))

# 购买会员功能
def buy_vip(client):
    if user_info.get('is_vip'):
        print('已经是会员了!')
        return

    is_vip = input('购买会员(y or n)?').strip()
    if is_vip == 'y':
        send_dic = {
            'type': 'buy_vip',
            'session': user_info.get('cookies')
        }

        back_dic = common.send_msg_back_dic(
            send_dic, client)

        if back_dic.get('flag'):
            print(back_dic.get('msg'))



    else:
        print('*穷*,快去打工赚钱!')

# 查看所有电影
def check_all_movie(client):
    send_dic = {
        'type': 'get_movie_list',
        'session': user_info.get('cookies')
    }

    back_dic = common.send_msg_back_dic(
        send_dic, client)
    if back_dic.get('flag'):
        print(back_dic.get('back_movie_list'))
    else:
        print(back_dic.get('msg'))

# 下载免费电影
def download_free_movie(client):
    while True:
        # 1.获取服务端所有免费电影
        send_dic = {
            'type': 'get_movie_list',
            'session': user_info.get('cookies'),
            'movie_type': 'free'
        }
        back_dic = common.send_msg_back_dic(send_dic, client)
        if back_dic.get('flag'):
            # 2.选择下载的免费电影,并提交给服务端
            movie_list = back_dic.get('back_movie_list')

            for index, movie in enumerate(movie_list):
                print(index, movie)

            choice = input('请输入下载电影编号:').strip()

            if not choice.isdigit():
                continue

            choice = int(choice)

            if choice not in range(len(movie_list)):
                continue

            movie_name, movie_type, movie_id = movie_list[choice]

            send_dic = {'type': 'download_movie',
                        'session': user_info.get('cookies'),
                        'movie_id': movie_id,
                        'movie_name': movie_name,
                        'movie_type': movie_type}

            back_dic = common.send_msg_back_dic(send_dic, client)

            if back_dic.get('flag'):
                # 3.开始下载电影
                movie_path = os.path.join(settings.DOWNLOAD_FILES, movie_name)
                movie_size = back_dic.get('movie_size')

                # 准备下载电影: 判断是否是VIP,若不是则等待广告播放
                wait_time = back_dic.get('wait_time')

                if wait_time:
                    print('惠州某工厂上线啦....')
                    time.sleep(wait_time)

                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)
                    f.flush()

                print('免费电影下载成功!')
                break

        else:
            print(back_dic.get('msg'))
            break


# 下载收费电影
def download_pay_movie(client):
    while True:

        if user_info.get('is_vip'):
            is_pay = input('VIP打骨折,收费5$一部(y or n):').strip()
        else:
            is_pay = input('普通用户,收费50$一部(y or n):').strip()

        if not is_pay == 'y':
            print('Gun去充钱!')
            break

        # 1.获取服务端所有免费电影
        send_dic = {
            'type': 'get_movie_list',
            'session': user_info.get('cookies'),
            'movie_type': 'pay'
        }
        back_dic = common.send_msg_back_dic(send_dic, client)
        if back_dic.get('flag'):
            # 2.选择下载的免费电影,并提交给服务端
            movie_list = back_dic.get('back_movie_list')

            for index, movie in enumerate(movie_list):
                print(index, movie)

            choice = input('请输入下载电影编号:').strip()

            if not choice.isdigit():
                continue

            choice = int(choice)

            if choice not in range(len(movie_list)):
                continue

            movie_name, movie_type, movie_id = movie_list[choice]

            send_dic = {'type': 'download_movie',
                        'session': user_info.get('cookies'),
                        'movie_id': movie_id,
                        'movie_name': movie_name,
                        'movie_type': movie_type}

            back_dic = common.send_msg_back_dic(send_dic, client)

            if back_dic.get('flag'):
                # 3.开始下载电影
                movie_path = os.path.join(settings.DOWNLOAD_FILES, movie_name)
                movie_size = back_dic.get('movie_size')

                # 准备下载电影: 判断是否是VIP,若不是则等待广告播放
                wait_time = back_dic.get('wait_time')

                if wait_time:
                    time.sleep(wait_time)

                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)
                    f.flush()

                print('收费电影下载成功!')
                break

        else:
            print(back_dic.get('msg'))
            break

# 查看下载记录功能
def check_download_record(client):
    send_dic = {'type': 'check_download_record',
                'session': user_info.get('cookies')}

    back_dic = common.send_msg_back_dic(send_dic, client)

    if back_dic.get('flag'):
        # 返回电影下载记录
        print(back_dic.get('record_list'))

    else:
        print(back_dic.get('msg'))

# 查看所有公告
def check_all_notice(client):
    send_dic = {'type': 'check_all_notice',
                'session': user_info.get('cookies')}
    back_dic = common.send_msg_back_dic(send_dic, client)
    if back_dic.get('flag'):
        print(back_dic.get('back_notice_list'))
    else:
        print(back_dic.get('msg'))

func_dic = {
    '1': register,
    '2': login,
    '3': buy_vip,
    '4': check_all_movie,
    '5': download_free_movie,
    '6': download_pay_movie,
    '7': check_download_record,
    '8': check_all_notice,
}

def user_view():
    sk_client = SocketClient()
    client = sk_client.get_client()
    while True:
        print('''
        1.注册
        2.登录
        3.充会员
        4.查看视频
        5.下载免费视频
        6.下载会员视频
        7.查看观影记录
        8.查看所有公告
        ''')

        choice = input('请选择功能编号:').strip()

        if choice == 'q':
            break

        if choice not in func_dic:
            continue

        func_dic.get(choice)(client)
user.py

download_files

下载文件文本

lib

import json
import struct
from conf import settings
import os
import hashlib

def send_msg_back_dic(send_dic, client, file=None):
    data_bytes = json.dumps(send_dic).encode('utf-8')
    headers = struct.pack('i', len(data_bytes))
    client.send(headers)
    client.send(data_bytes)

    # 上传电影
    if file:
        with open(file, 'rb') as f:
            for line in f:
                # print(line)
                client.send(line)

    headers = client.recv(4)
    data_len = struct.unpack('i', headers)[0]
    data_bytes = client.recv(data_len)
    back_dic = json.loads(data_bytes.decode('utf-8'))
    return back_dic


def get_movie_list():
    if os.path.exists(settings.UPLOAD_FILES):
        movie_list = os.listdir(settings.UPLOAD_FILES)
        if movie_list:
            return movie_list

# 获取电影的md5值
def get_movie_md5(movie_path):

    md5 = hashlib.md5()
    # 截取电影的4个位置的md5值
    movie_size = os.path.getsize(movie_path)

    # 从电影的4个位置个截取10个bytes数据
    current_index = [
        0, movie_size // 3, (movie_size // 3) * 2,
        movie_size - 10
    ]

    with open(movie_path, 'rb') as f:

        for index in current_index:

            f.seek(index)
            data = f.read(10)
            md5.update(data)

    return md5.hexdigest()
common.py

tcp_client

import socket


class SocketClient:
    def __init__(self):
        self.client = socket.socket()
        self.client.connect(
            ('127.0.0.1', 9527)
        )

    def get_client(self):
        return self.client

# sk = SocketClient()
# client = sk.get_client()
socket_client

upload_files

上传文件文本

readme

start.py

import os
import sys
from core import src

sys.path.append(
    os.path.dirname(__file__)
)


if __name__ == '__main__':
    src.run()
start.py

猜你喜欢

转载自www.cnblogs.com/qinsungui921112/p/11576340.html
今日推荐