优酷服务端

服务端项目目录

conf

import os


BASE_DIR = os.path.dirname(os.path.dirname(__file__))
BASE_MOVIE_DIR = os.path.join(BASE_DIR,'movie_dir')
settings.py

db

from orm_pool.orm import Models, StringField, IntegerField


class User(Models):
    table_name = 'user'
    id = IntegerField('id', primary_key=True)
    name = StringField('name')
    password = StringField('password')
    is_locked = IntegerField('is_locked', default=0)
    is_vip = IntegerField('is_vip', default=0)
    user_type = StringField('user_type')
    register_time = StringField('register_time')


class Movie(Models):
    table_name = 'movie'
    id = IntegerField('id', primary_key=True)
    name = StringField('name',column_type='varchar(64)')
    path = StringField('path')
    is_free = IntegerField('is_free', default=1)
    is_delete = IntegerField('is_delete', default=0)
    create_time = StringField('create_time')
    user_id = IntegerField('user_id')
    file_md5 = StringField('file_md5')


class Notice(Models):
    table_name = 'notice'
    id = IntegerField('id', primary_key=True)
    name = StringField('name')
    content = StringField('content')
    user_id = IntegerField('user_id')
    create_time = StringField('create_time')


class DownloadRecord(Models):
    table_name = 'download_record'
    id = IntegerField('id', primary_key=True)
    user_id = IntegerField('user_id')
    movie_id = IntegerField('movie_id')
    create_time = StringField('create_time')
modles.py

interface

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


@common.login_auth
def release_notice(recv_dic,conn):
    title = recv_dic.get('title')
    content = recv_dic.get('content')
    user_id = recv_dic.get('user_id')
    obj = models.Notice(name=title,content=content,user_id=user_id,create_time=str(datetime.now()))
    obj.save()
    back_dic = {'flag':True,'msg':'发布公告成功'}
    common.send_back(conn,back_dic)


@common.login_auth
def check_movie(recv_dic,conn):
    movie_data = models.Movie.select(file_md5=recv_dic['file_md5'])
    if movie_data:
        back_dic = {'flag':False,'msg':'该电影已存在'}
    else:
        back_dic = {'flag':True,'msg':'该电影不存在,可以上传'}
    common.send_back(conn,back_dic)


@common.login_auth
def upload_movie(recv_dic,conn):
    # 这里为了避免上传的视频名字是一样的但是内容不一样,所以文件名应该尽量取的唯一
    file_name = common.get_session(recv_dic.get('file_name'))+recv_dic.get('file_name')
    file_path = os.path.join(settings.BASE_MOVIE_DIR,file_name)
    recv_size = 0
    with open(file_path,'wb') as f:
        while recv_size < recv_dic['file_size']:
            recv_data = conn.recv(1024)
            f.write(recv_data)
            recv_size += len(recv_data)
    # 调用orm完成保存操作
    movie_obj = models.Movie(name=file_name,path=file_path,is_free=recv_dic.get("is_free"),is_delete=0,
                             create_time = common.get_nowtime(),user_id = recv_dic.get('user_id'),
                             file_md5=recv_dic.get("file_md5")
                             )
    movie_obj.save()
    back_dic = {'flag':True,'msg':'上传成功'}
    common.send_back(conn,back_dic)


@common.login_auth
def delete_movie(recv_dic,conn):
    # 删除电影并不是真的将电影数据删除 而仅仅是将标示该电影是否删除的字段改为1
    movie_list = models.Movie.select(id=recv_dic.get("delete_movie_id"))
    movie_obj = movie_list[0]
    movie_obj.is_delete = 1
    movie_obj.update()
    back_dic = {'flag':True,'msg':'删除成功'}
    common.send_back(conn,back_dic)
admin_interface
from lib import common
from db import models
import os


@common.login_auth
def get_movie_list(recv_dic,conn):
    movie_list = models.Movie.select()
    if movie_list:
        back_movie_list = []
        for movie in movie_list:
            # 筛选出所有没有被删除的电影
            if not movie.is_delete:
                # 根据用户想要查询的电影类型进行划分
                if recv_dic['movie_type'] == 'all':
                    # 只要不是删除的电影都要  数据格式[[电影名,是否免费,数据id],[...],[...]]
                    back_movie_list.append([movie.name, '免费' if movie.is_free else '收费', movie.id])
                elif recv_dic['movie_type'] == 'free':
                    if movie.is_free:
                        back_movie_list.append([movie.name, '免费', movie.id])
                else:
                    if not movie.is_free:
                        back_movie_list.append([movie.name, '收费', movie.id])
        # 校验电影列表是否为空
        if not back_movie_list:
            back_dic = {"flag":False,'msg':"暂无符合要求的电影"}
        else:
            back_dic = {'flag':True,'movie_list':back_movie_list}
    else:
        back_dic = {"flag":True,'msg':'暂无电影'}
    common.send_back(conn,back_dic)


@common.login_auth
def buy_member(recv_dic, conn):
    user_list = models.User.select(id=recv_dic['user_id'])
    user_obj = user_list[0]
    user_obj.is_vip = 1
    user_obj.update()
    back_dic = {'flag': True, 'msg': '购买会员成功'}
    common.send_back(conn,back_dic)


@common.login_auth
def download_movie(recv_dic,conn):
    movie_list = models.Movie.select(id=recv_dic.get('movie_id'))
    if movie_list:
        movie_obj = movie_list[0]
        user_obj = models.User.select(id=recv_dic['user_id'])[0]
        # 下载免费视频需要判断当前用户是否是vip会员,如果不是客户端需要等待30秒才能继续下载
        wait_time = 0
        if recv_dic['movie_type'] == 'free':
            if user_obj.is_vip:
                wait_time = 0
            else:
                wait_time = 30
        back_dic = {'flag':True,"file_name":movie_obj.name,'file_size':os.path.getsize(movie_obj.path),'wait_time':wait_time}
        # 将下载记录记录到专门存放记录的表中
        down_record = models.DownloadRecord(user_id=user_obj.id,movie_id=movie_obj.id,create_time=common.get_nowtime())
        down_record.save()
        common.send_back(conn,back_dic)

        # 打开文件传输给客户端
        with open(movie_obj.path,'rb') as f:
            for line in f:
                conn.send(line)
    else:
        back_dic = {'flag':False,'msg':'该电影不存在'}
        common.send_back(conn,back_dic)



@common.login_auth
def check_download_record(recv_dic,conn):
    record_list = models.DownloadRecord.select(user_id=recv_dic['user_id'])
    back_record = []
    if record_list:
        for record in record_list:
            # 查出电影对应的电影名
            movie_obj = models.Movie.select(id=record.movie_id)[0]
            back_record.append(movie_obj.name)
        back_dic = {"flag":True,'record':back_record}
    else:
        back_dic = {'flag':False,'msg':"暂无观影记录"}
    common.send_back(conn,back_dic)


@common.login_auth
def check_notice(recv_dic,conn):
    notice_list = check_notice_by_count(count=None)
    if notice_list:
        back_dic = {'flag': True, 'notice_list': notice_list}
    else:
        back_dic = {'flag': False, 'msg': '暂无公告'}

    common.send_back(conn, back_dic)


# 定义专门用来获取公告的方法
def check_notice_by_count(count=None):
    notice_list = models.Notice.select()
    back_notice_list = []
    if notice_list:  # 不为空,继续查询,为空直接返回false
        if not count:
            for notice in notice_list:
                back_notice_list.append({notice.name: notice.content})
        else:  # 查一条
            notice_list = sorted(notice_list, key=lambda notice: notice.create_time,reverse=True)
            back_notice_list.append({notice_list[0].name: notice_list[0].content})
        return back_notice_list
    else:
        return False
user_interface
from lib import common
from db import models
from datetime import datetime
from TcpServer import user_data
from interface import user_interface


def register(recv_dic, conn):
    name = recv_dic.get('name')
    user_data = models.User.select(name=name)
    if user_data:
        back_dic = {"flag": False, 'msg': '用户名已存在'}
    else:
        password = recv_dic.get('password')
        user_type = recv_dic.get('user_type')
        user_obj = models.User(name=name, password=password, user_type=user_type, is_locked=0, is_vip=0,
                               register_time="%s" % datetime.now())
        user_obj.save()
        back_dic = {'flag': True, 'msg': '注册成功'}
    common.send_back(conn, back_dic)


def login(recv_dic, conn):
    name = recv_dic.get("name")
    user_list = models.User.select(name=name)
    if user_list:
        user_obj = user_list[0]
        if user_obj.user_type == recv_dic.get("user_type"):
            if user_obj.password == recv_dic.get('password'):
                back_dic = {'flag': True, 'msg': '登陆成功','is_vip':user_obj.is_vip}
                session = common.get_session(user_obj.name)
                back_dic['session'] = session
                # 这里需要存储用户的session以及user_id 客户端登陆成功后只会携带session过来
                # 我们除了校验他的合法身份之外,也应该存该用户数据库对于的主键值,方便后期获取该用户数据进行相应的修改
                user_data.mutex.acquire()
                user_data.live_user[recv_dic['addr']] = [session, user_obj.id]
                user_data.mutex.release()
                # 普通用户在登陆之后强制打印最新一条公告内容
                if recv_dic['user_type'] == 'user':
                    last_notice = user_interface.check_notice_by_count(1)
                    back_dic['last_notice'] = last_notice
            else:
                back_dic = {'flag': False, 'msg': "密码错误"}
        else:
            back_dic = {'flag':False,'msg':'用户类型不正确'}
    else:
        back_dic = {'flag': False, 'msg': '用户不存在'}
    common.send_back(conn, back_dic)
common_interface

lib

import json
import hashlib
import time
from functools import wraps
from TcpServer import user_data
import struct


def send_back(conn,back_dic):
    back_bytes = json.dumps(back_dic).encode('utf-8')
    header = struct.pack('i',len(back_bytes))
    conn.send(header)
    conn.send(back_bytes)


def get_session(name):
    # 为了保证生成的随机字符串是独一无二的这里用cpu执行时间加盐
    md = hashlib.md5()
    md.update(str(time.clock()).encode('utf-8'))
    md.update(name.encode('utf-8'))
    return md.hexdigest()


def login_auth(func):
    @wraps(func)
    def inner(*args,**kwargs):
        # args=(recv_dic,conn)
        for values in user_data.live_user.values():
            if args[0]['session'] == values[0]:
                # 如果当前用户存在且登陆 将该用户id放入recv_dic中
                args[0]['user_id'] = values[1]
                break
        if args[0].get("user_id"):
            func(*args,**kwargs)
        else:
            back_dic = {'flag':False,'msg':'请先登陆'}
            send_back(args[1],back_dic)
    return inner


def get_nowtime():
    now_time = time.strftime('%Y-%m-%d %X')
    return now_time
common

orm_pool

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='12345678',
    database='youku',
    charset='utf8',
    autocommit='True'
)
db_pool
import pymysql
from orm_pool.db_pool import POOL

class Mysql(object):
    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 select(self, sql, args=None):
        self.cursor.execute(sql, args)
        res = self.cursor.fetchall()
        return res

    def execute(self, sql, args):
        try:
            self.cursor.execute(sql, args)
        except BaseException as e:
            print(e)


if __name__ == '__main__':
    ms = Mysql()
    res = ms.select('select * from class where cid=%s',1)
    print(res)
mysql_pool
from orm_pool.mysql_pool import Mysql


class Field(object):
    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


class StringField(Field):
    def __init__(self, name, column_type='varchar(32)', primary_key=False, default=None):
        super().__init__(name, column_type, primary_key, default)


class IntegerField(Field):
    def __init__(self, name, column_type='int', primary_key=False, default=0):
        super().__init__(name, column_type, primary_key, default)


class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs):
        if name == 'Models':
            return type.__new__(cls, name, bases, attrs)
        table_name = attrs.get('table_name', name)  # 获取表名,如果用户没有自定义那么就用类名
        primary_key = None
        mappings = {}
        for k, v in attrs.items():
            if isinstance(v, Field):  # 判断是否我们自定义的属性table_name,id,name等...
                mappings[k] = v
                if v.primary_key:
                    if primary_key:
                        raise TypeError("一张表有且只有一个主键")
                    primary_key = k  # 判断是否是主键
        # 将单个单个的自定义的字段属性从attrs中
        for k in mappings.keys():
            attrs.pop(k)
        if not primary_key:
            raise TypeError('没有设置主键')
        # 将表的三个特性表名,主键,字段付给类的属性
        attrs['table_name'] = table_name
        attrs['primary_key'] = primary_key
        attrs['mappings'] = mappings
        return type.__new__(cls, name, bases, attrs)


class Models(dict, metaclass=ModelMetaClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def __getattr__(self, item):
        # 字典对象获取值
        return self.get(item, '没有该键值对!')

    def __setattr__(self, key, value):
        # 字段对象设置值
        self[key] = value

    @classmethod
    def select(cls, **kwargs):
        ms = Mysql()
        if not kwargs:
            # 第一种查询可能:select * from userinfo;
            sql = "select * from %s" % cls.table_name
            res = ms.select(sql)  # 结果肯定是[{},{},{}]列表套字典
        else:
            # 第二种查询可能:select * from userinfo where id=%s或name=%s或password=%s...;
            key = list(kwargs.keys())[0]  # 这里我们规定过滤条件只能有一个,所以这里只取一个
            value = kwargs.get(key)
            sql = "select * from %s where %s=?" % (cls.table_name, key)
            sql = sql.replace('?', '%s')
            res = ms.select(sql, value)  # 结果肯定是[{},{},{}]列表套字典
        if res:
            # 列表推导式获取到一个个字典,**打散成name='jason',password='123'形式,传入类中完成实例化
            return [cls(**r) for r in res]  # 返回结果为[obj1,obj2,obj3...]

    def update(self):
        ms = Mysql()
        # update user set name='jason',password='123' where id=1;  更新操作where后统一就用主键
        fields = []
        pr = None
        args = []
        for k,v in self.mappings.items():
            if v.primary_key:
                pr = getattr(self,v.name,v.default)
            else:
                fields.append(v.name+'=?')
                args.append(getattr(self,v.name,v.default))
        sql = "update %s set %s where %s=%s"%(self.table_name,','.join(fields),self.primary_key,pr)
        # 上句的拼接结果update user set name=?,password=? where id=1
        sql = sql.replace('?','%s')
        ms.execute(sql,args)

    def save(self):
        ms = Mysql()
        # insert into user(name,password) values('jason','123')
        fields = []
        args = []
        values = []
        for k,v in self.mappings.items():
            if not v.primary_key:
                fields.append(v.name)
                args.append('?')
                values.append(getattr(self,v.name,v.default))
        sql = "insert into %s(%s) values(%s)"%(self.table_name,','.join(fields),','.join(args))
        # sql = "insert into user(name,password) values(?,?)"
        sql = sql.replace('?','%s')
        ms.execute(sql,values)
orm

orm_singleton

TcpServer

import socket
import json
from db import models
from datetime import datetime
from lib import common
from interface import admin_interface,user_interface,common_interface
from concurrent.futures import ThreadPoolExecutor
from threading import Lock
from TcpServer import user_data
import struct

# 对于报错消息是Address already in use
from socket import SOL_SOCKET,SO_REUSEADDR



pool = ThreadPoolExecutor(20)
mutex = Lock()
# 将生成的锁放到user_data,避免出现交叉导入的现象,这里导了common_interface,common_interface中又要导这里
user_data.mutex = mutex


func_dic = {
    'register':common_interface.register,
    'login':common_interface.login,
    'release_notice':admin_interface.release_notice,
    'check_movie':admin_interface.check_movie,
    'upload_movie':admin_interface.upload_movie,
    'get_movie_list':user_interface.get_movie_list,
    'delete_movie':admin_interface.delete_movie,
    'buy_member':user_interface.buy_member,
    'download_movie':user_interface.download_movie,
    'check_download_record':user_interface.check_download_record,
    'check_notice':user_interface.check_notice
}

def get_server():
    server = socket.socket()
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 在bind之前设置,避免服务重启时避免报Address already in use
    server.bind(('127.0.0.1',8082))
    server.listen(5)
    while True:
        conn,addr = server.accept()
        pool.submit(working,conn,addr)





def working(conn,addr):
    while True:
        try:
            recv_header = conn.recv(4)
            recv_bytes = conn.recv(struct.unpack('i',recv_header)[0])
            recv_dic = json.loads(recv_bytes.decode('utf-8'))
            # 在执行分发方法之前,将当前用户的addr也放入接受的到的字典中
            recv_dic['addr'] = str(addr)  # 由于addr不是字符串,这里手动转一下,后面需要拿它作为字典的key
            dispatch(recv_dic,conn)
        except Exception as e:
            print(e)
            conn.close()
            user_data.mutex.acquire()
            user_data.live_user.pop(str(addr))
            user_data.mutex.release()
            break


def dispatch(recv_dic,conn):
    if recv_dic['type'] in func_dic:
        func_dic.get(recv_dic['type'])(recv_dic,conn)
    else:
        back_dic = {'flag':False,'msg':'请求不合法!'}
        common.send_back(conn,back_dic)
tcpserver
live_user = {}
mutex = None
user_data

 

import os
import sys


BASE_DIR = os.path.dirname(__file__)
sys.path.append(BASE_DIR)
from TcpServer import tcpserver


if __name__ == '__main__':
    tcpserver.get_server()
start.py

 

 

posted @ 2019-05-20 22:54  JasonJi  阅读(1237)  评论(0编辑  收藏  举报