Title

仿优酷项目

项目三:仿优酷系统

一:项目需求

管理员

  • 1 注册

  • 2 登录

  • 3 上传视频

  • 4 删除视频

  • 5 发布公告

用户

  • 1 注册

  • 2 登录

  • 3 冲会员

  • 4 查看视频

  • 5 下载免费视频

  • 6 下载收费视频

  • 7 查看下载记录

  • 8 查看公告

二:项目理解

1.什么是ORM:

对象-关系映射(Object/Relation Mapping,简称ORM)

2.为什么要有ORM

我们需要将数据存到数据库中,但是又需要通过数据来调用一些属性方法,通过rom我们能解决将数据转换成对象进行操作,操作完再将对象转换成数据存到数据库中。

理论上我们需要提前建立好数据库表,但是建立表我们依赖于数据,数据有哪些字段我们的表就需要建立哪些字段。因此建立表之前我们需要建立一个models层,models里面有user类,我们就建user表,几个类就建立几个表,表名就是通过类名来的。表有两个属性,一个是表名,一个是字段,表名就是对应的类名,类中的对象通过__class__就能获得类,再__name__就能获得类名,这样同时也能获得表名,类中的属性就相当于表中的字段,类的对象也包含了数据,就相当于表的记录。

演示映射关系:

类<------->表

对象<----.>记录

属性<---->字段

问题1:如何通过类来创建一张有字段的表?什么时候建立一张表?又如何将对象与记录之间进行相互转换(增删改查)

因此需要封装一套orm,对象和记录经过orm就能相互转换

当我们产生一个模型类的时候

问题2:orm框架的增删改成操作的是数据字典还是模型类对象?

是模型类对象

问题3:在设计orm时要用到哪些知识点?

1元类控制类的创建

2.面向对象三大特性:封装、继承、多态

3.自定义异常

4.re模块

问题4:实现带连接池的Mysql工具类

1.工具类之类方法

2.time模块

3.多线程

4.连接池概念

三:具体项目

客户端:

start.py
1 import sys
2 import os
3 
4 sys.path.append(os.path.dirname(__file__))
5 
6 from core import src
7 
8 if __name__ == '__main__':
9     src.run()
start.py
tcp_client文件夹
1 import socket
2 
3 # socket客户端函数
4 def get_client():
5     client = socket.socket()
6     client.connect(
7         ('127.0.0.1', 9981)
8     )
9     return client
socket_client
lib文件夹:
 1 import json
 2 import struct
 3 import os
 4 import hashlib
 5 
 6 # 定义发送与接收数据的公共方法
 7 def send_and_back(send_dic,client, file=None):
 8     # 先序列化,得到json数据,并转成bytes数据
 9     json_bytes = json.dumps(send_dic).encode('utf-8')
10     # 给bytes数据制作报头
11     headers = struct.pack('i', len(json_bytes))
12     # 将打包好的数据发送给服务端
13     # 先发送报头
14     client.send(headers)
15     # 再发送真实数据(bytes数据)
16     client.send(json_bytes)
17 
18     # 注意,若上传电影时调用此功能打开文件上传数据
19     if file:
20         # 以bytes模式打开电影文件,循环传入
21         with open(file, 'rb') as f:
22             for line in f:
23                 client.send(line)
24 
25     # 接收服务端返回的数据
26     # 先接收报头
27     headers = client.recv(4)
28 
29     # 解压报头,获取真实数据的真实长度
30     # struct.unpack得到一个元组
31     bytes_len = struct.unpack('i', headers)[0]
32 
33 
34 
35     # 通过长度接收真实数据并解码
36     json_bytes_data = client.recv(bytes_len).decode('utf-8')
37 
38     # 反序列化得到真实数据字典
39     back_dic = json.loads(json_bytes_data)
40 
41     # 将数据返回出去
42     return back_dic
43 
44 
45 # 定义一个获取md5值的方法
46 def get_movie_md5(movie_path):
47     # 获取电影大小
48     movie_size = os.path.getsize(movie_path)
49 
50     # 指定位置截取字段生成MD5值
51     index_list = [0, movie_size//3, (movie_size//3)*2, movie_size-10]
52 
53     # 获取md5加密对象
54     md5_obj = hashlib.md5()
55 
56     # 先打开电影获取真实数据
57     with open(movie_path, 'rb') as f:
58         for line in index_list:
59         # 移动光标到指定位置
60             f.seek(line)
61         # 每个位置获取10个bytes数据
62             data = f.read(10)
63         # 每10个bytes 加一次密
64             md5_obj.update(data)
65 
66     return md5_obj.hexdigest()
common.py
core文件夹:
  1 '''
  2 管理员视图
  3 '''
  4 from lib import common
  5 from conf import settings
  6 import os
  7 
  8 user_info = {
  9     'cookies': None
 10 }
 11 
 12 
 13 # 管理员注册功能
 14 def register(client):
 15     while True:
 16         # 输入用户名和密码
 17         username = input('请输入用户名: ').strip()
 18         password = input('请输入密码: ').strip()
 19         re_password = input('请确认密码: ').strip()
 20         # 校验两次密码是否相等
 21         if password == re_password:
 22             # 组织客户端的注册数据
 23             send_dic = {
 24                 'username': username,
 25                 'password': password,
 26                 'user_type': 'admin',
 27                 'func_type': 'register'
 28             }
 29             # 将组织好的字典发送到服务端并接收返回结果
 30             back_dic = common.send_and_back(send_dic, client)
 31 
 32             # 判断服务端返回来的字典信息内是否有注册成功,并打印消息
 33             print('运行测试34')
 34             if back_dic.get('flag'):
 35                 print(back_dic.get('msg'))
 36                 break
 37             else:
 38                 print(back_dic.get('msg'))
 39 
 40 
 41 # 管理员登陆功能
 42 def login(client):
 43     while True:
 44         username = input('请输入用户名: ').strip()
 45         password = input('请输入密码: ').strip()
 46         # 1.组织数据发送给服务端
 47         send_dic = {
 48             'username': username,
 49             'password': password,
 50             'func_type': 'login'
 51         }
 52         # 接收服务端返回的消息
 53         back_dic = common.send_and_back(send_dic, client)
 54         if back_dic.get('flag'):
 55             print(back_dic.get('msg'))
 56             user_info['cookies'] = back_dic.get('session')
 57             break
 58 
 59         else:
 60             print(back_dic.get('msg'))
 61 
 62 
 63 # 管理员上传电影
 64 def upload_movie(client):
 65     while True:
 66         # 获取电影文件夹位置
 67         upload_movie_dir = settings.UPLOAD_MOVIE_DIR
 68         # 打印目录所有的电影文件,让用户选择
 69         movie_list = os.listdir(upload_movie_dir)
 70 
 71         # 若没有可上传的电影
 72         if not movie_list:
 73             print('没有可上传的电影!!!')
 74             break
 75         # 获取电影编码和名字
 76         for index, movie_name in enumerate(movie_list):
 77             print(index, movie_name)
 78         print('选择q可以退出')
 79         choice = input('请输入上传电影编号: ').strip()
 80         if choice == 'q':
 81             break
 82         if not choice.isdigit():
 83             continue
 84         choice = int(choice)
 85         if choice not in range(len(movie_list)):
 86             continue
 87         # 获取到电影名
 88         movie_name = movie_list[choice]
 89         # 获取真实的电影数据
 90         movie_path = os.path.join(upload_movie_dir, movie_name)
 91         # 组织需要上传的电影信息发送给服务端
 92         # 获取电影大小
 93         movie_size = os.path.getsize(movie_path)
 94         # 获取电影md5值,发送给服务端,校验电影是否存在
 95         movie_md5 = common.get_movie_md5(movie_path)
 96         # 发送检测电影是否存在数据给服务端
 97         send_dic = {
 98             'func_type': 'check_movie',
 99             'movie_md5': movie_md5,
100             'cookies': user_info.get('cookies')
101         }
102         # 获取返回的消息
103         back_dic = common.send_and_back(send_dic, client)
104 
105         # 校验back_dic中返回的消息,若电影不存在则发送上传电影功能申请
106         if back_dic.get('flag'):
107             # 管理员设置上传的电影是否免费
108             print('电影收费选择y,免费选择n')
109             choice2 = input('请确认电影是否收费: ').strip()
110             is_free = 1
111             # 若输入Y,将is_free修改成0
112             if choice2 == 'y':
113                 is_free = 0
114             send_dic = {
115                 'func_type': 'upload_movie',
116                 'movie_name': movie_name,
117                 'movie_size': movie_size,
118                 'movie_md5': movie_md5,
119                 'cookies':user_info.get('cookies'),
120                 'is_free': is_free
121             }
122             # 将信息发送过去
123             back_dic2 = common.send_and_back(send_dic, client, file=movie_path)
124             if back_dic2.get('flag'):
125                 print(back_dic2.get('msg'))
126                 break
127         else:
128             print(back_dic.get('msg'))
129 
130 
131 # 管理员删除电影
132 def delete_movie(client):
133     while True:
134         # 1.先去服务端获取可删除的电影(注意携带cookies)
135         send_dic = {
136             'func_type': 'get_movie_list',
137             'cookies': user_info.get('cookies'),
138             'movie_type': 'all'
139         }
140         # 获取返回的消息
141         back_dic = common.send_and_back(send_dic, client)
142         # 若没有电影则退出
143         if not back_dic.get('flag'):
144             print(back_dic.get('msg'))
145             break
146         # 若有则打印并选择可删除的电影
147         movie_list = back_dic.get('movie_list')
148         for index, movie_name_id in enumerate(movie_list):
149             print(index, movie_name_id)
150         choice = input('请需要删除的电影编号: ').strip()
151         if choice == 'q':
152             break
153         if not choice.isdigit():
154             continue
155         choice = int(choice)
156 
157         if choice not in range(len(movie_list)):
158             continue
159         # 3.发送删除的请求给服务端,让服务端修改电影的is_delete字段即可
160         # movie_name_id ---> [电影名字, 电影id]
161         movie_name_id = movie_list[choice]
162         # 制作信息
163         send_dic = {
164             'func_type': 'delete_movie',
165             'cookies': user_info.get('cookies'),
166             'movie_id': movie_name_id[1]  # 传入唯一的电影id
167         }
168         # 获取返回的结果
169         back_dic2 = common.send_and_back(send_dic, client)
170         if back_dic2.get('flag'):
171             print(back_dic2.get('msg'))
172             break
173 
174 # 管理员发布公告
175 def send_notice(client):
176 
177     # 1.让管理员输入 公告标题与内容
178     title = input('请输入公告标题:').strip()
179 
180     # 2.发送数据给服务端保存公告即可
181     content = input('请输入公告内容:').strip()
182 
183     send_dic = {
184         'cookies': user_info.get('cookies'),
185         'title': title,
186         'content': content,
187         'func_type': 'send_notice'
188     }
189 
190     back_dic = common.send_and_back(send_dic, client)
191     print(back_dic.get('msg'))
192 
193 
194 
195 
196 
197 func_dic = {
198     '1': register,
199     '2': login,
200     '3': upload_movie,
201     '4': delete_movie,
202     '5': send_notice,
203 }
204 
205 def admin_view(client):
206     while True:
207         print('''
208     1.注册
209     2.登陆
210     3.上传视频
211     4.删除视频
212     5.发布公告
213     q.退出
214     ''')
215         choice = input('请输入功能编号: ').strip()
216         if choice == 'q':
217             break
218         if choice not in func_dic:
219             continue
220         func_dic.get(choice)(client)
admin.py
 1 from tcp_client import socket_client
 2 from core import admin
 3 from core import user
 4 
 5 
 6 func_dic = {
 7     '1': admin.admin_view,
 8     '2': user.user_view
 9 }
10 
11 # 总视图run函数
12 def run():
13     print('Client is running...')
14     # 获得client套接字
15     client = socket_client.get_client()
16     while True:
17         print('''
18         1.管理员视图
19         2.普通用户视图
20         q.退出
21         ''')
22 
23         # 开始 输入选择功能编号
24         choice = input('请输入功能编号: ').strip()
25         if choice == 'q':
26             break
27         if choice not in func_dic:
28             continue
29         func_dic.get(choice)(client)
30 
31     # 关闭通道
32     client.close()
src.py
  1 '''
  2 用户视图
  3 '''
  4 from lib import common
  5 import time
  6 import os
  7 from conf import settings
  8 
  9 user_info = {
 10     'cookies': None,
 11     'is_vip': 0  # 默认登录前不是VIP
 12 }
 13 
 14 
 15 # 普通用户注册视图(和管理员一样)
 16 def register(client):
 17     while True:
 18         username = input('请输入用户名: ').strip()
 19         password = input('请输入密码: ').strip()
 20         re_password = input('请确认密码: ').strip()
 21         if password == re_password:
 22             # 一 组织客户端的注册数据
 23             send_dic = {
 24                 'username': username,
 25                 'password': password,
 26                 'user_type': 'user',
 27                 'func_type': 'register'
 28             }
 29 
 30             back_dic = common.send_and_back(send_dic, client)
 31 
 32             # 判断服务端返回来的字典是否注册成功,并打印消息
 33             if back_dic.get('flag'):
 34                 print(back_dic.get('msg'))
 35                 break
 36 
 37             else:
 38                 print(back_dic.get('msg'))
 39 
 40 
 41 #  普通用户登录
 42 def login(client):
 43     while True:
 44         username = input('请输入用户名: ').strip()
 45         password = input('请输入密码: ').strip()
 46 
 47         # 1.组织数据发送给服务端
 48         send_dic = {
 49             'username': username,
 50             'password': password,
 51             'func_type': 'login'
 52         }
 53 
 54         # 调用commin中的send_and_back与服务端交互
 55         back_dic = common.send_and_back(send_dic, client)
 56 
 57         if back_dic.get('flag'):
 58             print(back_dic.get('msg'))
 59             # 修改cookies值
 60             user_info['cookies'] = back_dic.get('session')
 61             # 修改is_vip值
 62             is_vip = back_dic.get('is_vip')
 63             if is_vip:
 64                 user_info['is_vip'] = is_vip
 65             break
 66 
 67         else:
 68             print(back_dic.get('msg'))
 69 
 70 
 71 #  普通用户充会员
 72 def buy_vip(client):
 73     while True:
 74         # 判断当前用户是不是会员
 75         if not user_info.get('is_vip'):
 76             choice = input('请输入y 或者 n 确认是否购买价值5000元的黑金会员: ').strip()
 77             if choice not in ['y', 'n']:
 78                 print('输入错误')
 79                 break
 80             if choice == 'y':
 81                 # 制作消息字典给服务端修改数据is_vip字段
 82                 send_dic = {
 83                     'func_type': 'buy_vip',
 84                     'cookies': user_info.get('cookies')
 85                 }
 86                 back_dic = common.send_and_back(send_dic, client)
 87                 if back_dic.get('flag'):
 88                     print(back_dic.get('msg'))
 89                     print('请重新登陆跟新数据')
 90                     break
 91                 else:
 92                     print(back_dic.get('msg'))
 93                     break
 94             else:
 95                 print('请努力赚钱,欢迎下次光临')
 96                 break
 97         else:
 98             print('您已经是尊贵的黑金会员了!!!')
 99             break
100 
101 
102 #  普通用户查看电影
103 def check_movies(client):
104     # 1.先去服务端获取可删除的电影
105     send_dic = {
106         'func_type': 'get_movie_list',
107         'movie_type': 'all',
108         'cookies': user_info.get('cookies')
109     }
110 
111     back_dic = common.send_and_back(send_dic, client)
112     # 直接打印消息就可以
113     if back_dic.get('flag'):
114         print(back_dic.get('movie_list'))
115 
116 
117 #  普通用户下载免费电影
118 def download_free_movie(client):
119     while True:
120         # 先去服务端获取所有免费的电影
121         send_dic = {
122             'func_type': 'get_movie_list',
123             'movie_type': 'free',
124             'cookies': user_info.get('cookies')
125         }
126 
127         # 获取返回结果
128         back_dic = common.send_and_back(send_dic, client)
129 
130         # 若服务端没有电影
131         if not back_dic.get('flag'):
132             print(back_dic.get('msg'))
133             break
134         # 获取电影列表
135         movie_list = back_dic.get('movie_list')
136         # 若服务端有电影但是没有免费电影
137         if not movie_list:
138             print('没有可下载的免费电影')
139             break
140 
141         # 否则打印所有的免费电影并让用户选择
142         for index, name_id_type in enumerate(movie_list):
143             print(index, name_id_type)
144 
145         # 输入需要下载的电影编号
146         choice = input('请输入下载电影的编号: ').strip()
147         if not choice.isdigit():
148             print('请输入正确编号!!')
149             continue
150 
151         choice = int(choice)
152 
153         if choice not in range(len(movie_list)):
154             print('请输入正确编号!!')
155             continue
156 
157         movie_name, movie_id, movie_type = movie_list[choice]
158         # 可查看一下打印的内容
159         print(movie_name, movie_id, movie_type)
160 
161         # 选择成功后,制作下载免费电影的请求给服务端
162         send_dic2 = {
163             'func_type': 'download_movie',
164             'movie_name': movie_name,
165             'movie_id': movie_id,
166             'cookies': user_info.get('cookies')
167         }
168         back_dic2 = common.send_and_back(send_dic2, client)
169 
170         # 免费电影可设置广告时间,模拟广告等待
171         wait_time = back_dic2.get('wait_time')
172         if wait_time:
173             print('广告时间:{你想学编程吗?你想月入百万吗?你想迎娶白富美吗,欢迎来上海老男孩全球IT技术培训集团')
174         time.sleep(wait_time)
175 
176         # 接收服务端返回来的下载电影字典
177         movie_size = back_dic2.get('movie_size')
178 
179         # 接收电影真实数据,并保存到download_movie_dir中
180         movie_path = os.path.join(
181             settings.DOWNLOAD_MOVIE_DIR, movie_name
182         )
183         # 开始正式接收电影
184         recv_data = 0
185         with open(movie_path, 'wb') as f:
186             while recv_data < movie_size:
187                 data = client.recv(1024)
188                 f.write(data)
189                 recv_data += len(data)
190         print('电影下载成功')
191         break
192 
193 
194 #  普通用户下载收费电影
195 def download_buy_movie(client):
196     while True:
197         # 先去服务端获取所有收费的电影
198         send_dic = {
199             'func_type': 'get_movie_list',
200             'movie_type': 'pay',
201             'cookies': user_info.get('cookies')
202         }
203         # 获取返回结果
204         back_dic = common.send_and_back(send_dic, client)
205 
206         # 若服务端没有电影
207         if not back_dic.get('flag'):
208             print(back_dic.get('msg'))
209             break
210         # 获取电影列表
211         movie_list = back_dic.get('movie_list')
212         # 若服务端有电影但是没有免费电影
213         if not movie_list:
214             print('没有可下载的收费电影')
215             break
216 
217         # 否则打印所有的收费电影并让用户选择
218         for index, name_id_type in enumerate(movie_list):
219             print(index, name_id_type)
220 
221         # 设置电影收费项目,缴费才能选择电影
222         print('收费电影:普通用户100元一部,尊贵的黑金用户50元一部,确认购买请按"y","退出任意键')
223         choice_buy = input('请输入: ').strip()
224         if choice_buy == 'y':
225             if user_info.get('is_vip'):
226                 print('尊贵的会员恭喜您付费成功!')
227             else:
228                 print('恭喜您付费成功')
229 
230             print('现在请开始选则您喜欢的电影')
231         else:
232             print('欢迎下次光临')
233             break
234         # 输入需要下载的电影编号
235         choice = input('请输入下载电影的编号: ').strip()
236         if not choice.isdigit():
237             print('请输入正确编号!!')
238             continue
239 
240         choice = int(choice)
241 
242         if choice not in range(len(movie_list)):
243             print('请输入正确编号!!')
244             continue
245         movie_name, movie_id, movie_type = movie_list[choice]
246         # 可查看一下打印的内容
247         print(movie_name, movie_id, movie_type)
248 
249         # 选择成功后,制作下载收费电影的请求给服务端
250         send_dic2 = {
251             'func_type': 'download_movie',
252             'movie_name': movie_name,
253             'movie_id': movie_id,
254             'cookies': user_info.get('cookies')
255         }
256         back_dic2 = common.send_and_back(send_dic2, client)
257 
258         # 收费电影可不设置广告时间
259         # 接收服务端返回来的下载电影字典
260         movie_size = back_dic2.get('movie_size')
261         # 接收电影真实数据,并保存到download_movie_dir中
262         movie_path = os.path.join(
263             settings.DOWNLOAD_MOVIE_DIR, movie_name
264         )
265         # 开始正式接收电影
266         recv_data = 0
267         with open(movie_path, 'wb') as f:
268             while recv_data < movie_size:
269                 data = client.recv(1024)
270                 f.write(data)
271                 recv_data += len(data)
272         print('恭喜您下载电影成功')
273         break
274 
275 
276 #  普通用户查看下载记录
277 def check_movie_record(client):
278     # 制作想要查看下载记录的信息
279     send_dic = {
280         'func_type': 'check_movie_record',
281         'cookies': user_info.get('cookies')
282     }
283     back_dic = common.send_and_back(send_dic, client)
284     print(back_dic)
285 
286     if back_dic.get('flag'):
287         print(back_dic.get('download_movie_list'))
288     else:
289         print(back_dic.get('msg'))
290 
291 
292 #  普通用户查看公告
293 def check_notice(client):
294     # 制作发送给服务端查看公告的请求
295     send_dic = {
296         'func_type': 'check_notice',
297         'cookies': user_info.get('cookies')
298     }
299      # 接收服务端信息
300     back_dic = common.send_and_back(send_dic, client)
301 
302     if back_dic.get('flag'):
303         print(back_dic.get('notice_list'))
304     else:
305         print(back_dic.get('msg'))
306 
307 
308 func_dic = {
309     '1': register,
310     '2': login,
311     '3': buy_vip,
312     '4': check_movies,
313     '5': download_free_movie,
314     '6': download_buy_movie,
315     '7': check_movie_record,
316     '8': check_notice
317 }
318 
319 
320 def user_view(client):
321     while True:
322         print('''
323             1 注册
324             2 登录
325             3 冲会员
326             4 查看视频
327             5 下载免费视频
328             6 下载收费视频
329             7 查看下载记录
330             8 查看公告
331             q.退出
332         ''')
333 
334         choice = input('请输入功能编号:').strip()
335         if choice == 'q':
336             break
337 
338         if choice not in func_dic:
339             continue
340 
341         func_dic.get(choice)(client)
user.py
conf文件夹:
 1 import os
 2 
 3 BASE_PATH = os.path.dirname(os.path.dirname(__file__))
 4 
 5 UPLOAD_MOVIE_DIR = os.path.join(
 6     BASE_PATH, 'upload_movie_dir'
 7 )
 8 
 9 DOWNLOAD_MOVIE_DIR = os.path.join(
10     BASE_PATH, 'download_movie_dir'
11 )
settings

 

服务端:

start.py
 1 import sys
 2 import os
 3 sys.path.append(
 4     os.path.dirname(__file__)
 5 )
 6 
 7 from tcp_server import socket_server
 8 if __name__ == '__main__':
 9     print('Server in running...')
10 
11     socket_server.run()
start.py
tcp_server文件夹:
 1 import socket
 2 from concurrent.futures import ThreadPoolExecutor
 3 from db import user_data
 4 
 5 from interface import common_interface
 6 
 7 from interface import admin_interface
 8 from interface import user_interface
 9 from threading import Lock
10 import struct
11 import json
12 
13 
14 pool = ThreadPoolExecutor(50)
15 mutex = Lock()
16 
17 # 程序接口字典
18 
19 func_dic = {
20     # 注册接口
21     'register': common_interface.register_interface,
22     # 登陆接口
23     'login': common_interface.login_interface,
24     # 检测电影接口
25     'check_movie': admin_interface.check_movie_interface,
26     # 上传电影接口
27     'upload_movie': admin_interface.upload_movie_interface,
28     # 获取未删除电影接口
29     'get_movie_list': common_interface.get_movie_list_interface,
30     # 删除电影接口
31     'delete_movie': admin_interface.delete_movie_interface,
32     # 发布公告接口
33     'send_notice': admin_interface.send_notice_interface,
34     # 购买会员接口
35     'buy_vip': user_interface.buy_vip_interface,
36     # 下载电影接口
37     'download_movie': common_interface.download_free_movie_interface,
38     # 查看下载记录接口
39     'check_movie_record': user_interface.check_movie_record_interface,
40     # 查看公告接口
41     'check_notice': user_interface.check_notice_interface
42 }
43 
44 
45 # 定义任务函数
46 def working(conn, addr):
47 
48     while True:
49         # 获取报错信息并返回
50         try:
51             # 1.先接收报头
52             headers = conn.recv(4)
53             # 解压报头,获取真实数据长度
54             # struct.unpack得到一个元组
55             bytes_len = struct.unpack('i', headers)[0]
56             # 根据真实长度获取真实数据(json数据)
57             json_bytes_data = conn.recv(bytes_len).decode('utf-8')
58             # 反序列化得到真实数据字典
59             back_dic = json.loads(json_bytes_data)
60             # 保证每个客户端发过来都是唯一,将addr添加到客户端发送过来的字典中
61             back_dic['addr'] = str(addr)
62             # 获得客户端发送过来的执行程序的类型
63             func_type = back_dic.get('func_type')
64             # 查看此程序类型是否在程序接口字典中(建立一个程序接口字典)
65             if func_type in func_dic:
66                 # 如果有执行此接口,并将客户端传来的信息字典和conn传过去
67                 func_dic.get(func_type)(back_dic, conn)
68 
69 
70         # 捕获错误信息给e
71         except Exception as e:
72             print(e)
73             # 上锁
74             mutex.acquire()
75             # 清除当前客户端存放在服务端的session值
76             user_data.user_online.pop(str(addr))
77             # 解锁
78             mutex.release()
79             break
80     conn.close()
81 
82 
83 # 实现并发
84 def run():
85     # 获取socket套接字
86     server = socket.socket()
87     # 绑定ip和端口
88     server.bind(('127.0.0.1', 9981))
89     # 设置半连接池
90     server.listen(50)
91 
92     while True:
93         # 链接获取通道和地址
94         conn, addr = server.accept()
95         # 异步提交working任务(开启五十个线程)
96         pool.submit(working, conn, addr)
socket_Server
orm_control文件夹:
 1 import pymysql
 2 from orm_control.mysql_pool import POOL
 3 
 4 
 5 # MySQL连接类,链接数据库
 6 class MySQL:
 7     __instance = None
 8     # 使用单例模式
 9     @classmethod
10     def singleton(cls):
11         if not cls.__instance:
12             cls.__instance = cls()
13 
14         return cls.__instance
15 
16     # 实例化MySQL类时,获取数据库链接对象,获取游标对象
17     def __init__(self):
18         # 通过数据库连接池获取数据库链接对象
19         self.mysql_client = POOL.connection()
20         # 获取数据库游标对象并将结果以列表套字典的形式返回
21         self.cursor = self.mysql_client.cursor(
22             pymysql.cursors.DictCursor
23         )
24 
25 
26     # 自定义查询方法
27     def select(self, sql, args=None):
28         # 提交sql语句
29         self.cursor.execute(sql, args)
30         # 获取返回的结果
31         res = self.cursor.fetchall()
32         return res
33 
34     # 自定义提交sql语句方法,比如:insert,undate
35     def execute(self, sql, args):
36         # 提交sql语句
37         try:
38             self.cursor.execute(sql, args)
39         except Exception as e:
40             print(e)
41 
42     # 关闭数据库及游标
43     def close(self):
44         self.cursor.close()
45         # 再关闭数据库连接
46         self.mysql_client.close()
mysql_control.py
 1 from DBUtils.PooledDB import PooledDB
 2 import pymysql
 3 '''
 4 数据库连接池
 5 '''
 6 POOL = PooledDB(
 7     creator=pymysql,  # 使用链接数据库的模块
 8     maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
 9     mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
10     maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
11     maxshared=3,
12     # 链接池中最多共享的链接数量,0和None表示全部共享。
13     # PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
14     blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
15     maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
16     setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
17     ping=0,
18     # 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
19     host='127.0.0.1',
20     port=3306,
21     user='root',
22     password='960617',
23     database='orm_demo',
24     charset='utf8',
25     autocommit=True
26 )
mysql_pool.py
  1 '''
  2     ORM: 对象关系映射
  3         将对象 映射成 数据表中的一条条记录
  4 
  5     类 ----> 表名
  6     对象 ----> 记录
  7     对象.属性 ----> 字段
  8 '''
  9 from orm_control.mysql_control import MySQL
 10 
 11 
 12 # 建立一个字段类型的父类
 13 class Field:
 14     def __init__(self, name,
 15                  column_type,
 16                  primary_key,
 17                  default):
 18         self.name = name
 19         self.column_type = column_type
 20         self.primary_key = primary_key
 21         self.default = default
 22 
 23 
 24 # 字段类型为字符串的
 25 class StringField(Field):
 26     def __init__(self, name,
 27                  column_type='varchar(64)',
 28                  primary_key=False,
 29                  default=None):
 30         super().__init__(name, column_type, primary_key, default)
 31 
 32 
 33 # 字段类型为整型的
 34 class IntegerField(Field):
 35     def __init__(self, name, column_type='int',
 36                  primary_key=False,
 37                  default=0):
 38         super().__init__(name, column_type, primary_key, default)
 39 
 40 
 41 # 自定义元类,控制表类的创建
 42 # 1.保证一张表必须要有一个表名,
 43 # 2,一张表只能有一个主键,
 44 # 3.将所有的'字段名'与'字段对象'添加到一个独立的字典中
 45 # 以key(字段名):value(字段对象),添加到类的名称空间中,方便后期使用
 46 
 47 class OrmMetaClass(type):
 48 
 49     def __new__(cls, class_name, class_bases, class_attr):
 50         # 参数为继承元类的类名,该类的父类,该类的名称空间
 51         # 将Models类过滤掉
 52         if class_name == 'Models':
 53             # 将models类的类名,基类和民称空间原路返回
 54             return type.__new__(cls, class_name, class_bases, class_attr)
 55 
 56         # 获取table表名,若自定义则获取,没有则默认使用类名
 57         # dict.get(key) ---> key若有则返回对应的值,若没有则返回默认值 class_name就是默认值
 58         # 将类名当作表名
 59         table_name = class_attr.get('table_name', class_name)
 60         # 先设置默认主键值
 61         primary_key = None
 62         # 设置存放字段名与字段对象的字典
 63         mappings = {}
 64 
 65         # 保证一张表只能有一个主键
 66         # 循环遍历类的名称空间
 67         for k, v in class_attr.items():
 68             # 将字段以外的属性过滤
 69             # 判断当前的V是否是字段对象
 70             if isinstance(v, Field):
 71                 # 如果是则添加到字典中
 72                 mappings[k] = v
 73 
 74                 # 注意:字典被迭代时不能修改其属性
 75                 # class_attr.pop(k)
 76                 # 判断字段对象如果有主键,则为primary_key变量赋值
 77                 if v.primary_key:
 78                     # 再次判断primary_key是否有值,如果有则证明主键已有,抛出异常
 79                     if primary_key:
 80                         raise TypeError('一张表只能有一个主键')
 81                     # 赋值
 82                     primary_key = v.name
 83         # 过滤掉类名称空间中重复的字段属性
 84         for key in mappings.keys():
 85             class_attr.pop(key)
 86 
 87         # 检测一遍是否有主键
 88 
 89         if not primary_key:
 90             raise TypeError('一张表必须要有一个主键')
 91         # 给类的名称空间添加表名,主键和属性
 92         class_attr['table_name'] = table_name
 93         class_attr['primary_key'] = primary_key
 94         class_attr['mappings'] = mappings
 95         # 将类的名称空间返回出去
 96         return type.__new__(cls, class_name, class_bases, class_attr)
 97 
 98 
 99 # 定义一个类继承字典类,并且定义获取属性的方法
100 
101 class Models(dict, metaclass=OrmMetaClass):
102 
103     # 对象.属性,属性没有时触发
104     def __getattr__(self, item):
105         return self.get(item)
106 
107     # 对象.属性 = 属性值时触发
108     def __setattr__(self, key, value):
109         self[key] = value
110 
111     # 查询数据的方法(定义成类方法)
112     @classmethod
113     def select_data(cls, **kwargs):
114         # 定义一个MySQL类
115         # 获取链接数据库类的对象
116         mysql_obj = MySQL()
117         filed_value = None
118 
119         # 若kwargs为False代表没有查询条件
120         if not kwargs:
121             # 查所有数据
122             sql = f'select * from {cls.table_name}'
123         else:
124             # 根据条件查找
125             # 获取字段名
126             filed_name = list(kwargs.keys())[0]
127             print(filed_name)
128 
129             # 获取字段值
130             filed_value = kwargs.get(filed_name)
131 
132             # 根据条件查询
133             # select * from 表名 where 字段名=字段值;
134             sql = f'select * from {cls.table_name} where {filed_name}=?'
135             sql = sql.replace('?', '%s')
136         res = mysql_obj.select(sql, filed_value)  # res若为空则已注册
137         # 返回查询结果
138         return [cls(**r) for r in res]
139 
140     # 插入数据
141     def insert_data(self):
142         mysql_obj = MySQL()
143 
144         # 存放字段名的列表
145         filed_names = []  # [字段名, 字段名, 字段名, ]
146 
147         # 存放字段值的列表
148         filed_values = []  # [字段值, 字段值,字段值,]
149 
150         # 设置一个替换值的列表
151         replace_list = []  # [?, ?]
152 
153         # 获取民粹空间中所有的数据
154         for k, v in self.mappings.items():
155             # 获取字段名,追加到列表中
156             filed_names.append(v.name)
157             res = self.get(v.name, v.default)
158             # print(f'{v.name}', res)
159             filed_values.append(
160                 # 反射: 根据字符串操作对象中的属性或方法
161                 res
162             )
163             replace_list.append('?')
164         #  # sql = f'insert into {self.table_name}({",".join(filed_names)}) values({",".join(replace_list)})'
165         # 组织sql语句
166         sql = 'insert into %s(%s) values(%s)' % (
167             self.table_name, ",".join(filed_names), ",".join(replace_list)
168         )
169         sql = sql.replace('?', '%s')
170         # 调用mysql语句执行方法
171         mysql_obj.execute(sql, filed_values)
172 
173     # 跟新数据
174     def update_data(self):
175         mysql_obj = MySQL()
176         # 主键值
177         pk = None
178         # 存放字段名的列表
179         filed_names = []  # [字段名, 字段名, 字段名, ]
180 
181         # 存放字段值的列表
182         filed_values = []  # [字段值, 字段值,字段值,]
183 
184         for k, v in self.mappings.items():
185             # 判断mappings中哪一个字段是主键
186             if v.primary_key:
187                 # 获取主键的值
188                 pk = self.get(v.name)
189 
190             else:
191                 # 添加字段名
192                 filed_names.append(v.name + '=?')
193 
194                 # 添加字段值
195                 filed_values.append(
196                     self.get(v.name, v.default)
197                 )
198         # sql: update User set 字段名=字段值, 字段名=字段值
199         # sql: update User set username=?, password=? where pk=1;
200         sql = 'update %s set %s where %s=%s' % (
201             self.table_name,
202             ','.join(filed_names),
203             self.primary_key,
204             pk
205         )
206         sql = sql.replace('?', '%s')
207         mysql_obj.execute(sql, filed_values)
orm.py
lib文件夹:
 1 import struct
 2 import json
 3 import hashlib
 4 import uuid
 5 from db.user_data import user_online
 6 from threading import Lock
 7 
 8 mutex = Lock()
 9 
10 
11 # 服务端发送消息给客户端的公共方法
12 def send_data(back_dic, conn, file=None):
13 
14     # 1序列化得到json的bytes数据
15     bytes_data = json.dumps(back_dic).encode('utf-8')
16 
17 
18 
19 
20     # 制作报头
21     headers = struct.pack('i', len(bytes_data))
22 
23 
24 
25 
26     # 先发送报头
27 
28     conn.send(headers)
29 
30 
31     # 再发送真实数据
32     conn.send(bytes_data)
33 
34     if file:
35         # 循环发送
36         with open(file, 'rb') as f:
37             for line in f:
38                 conn.send(line)
39 
40 
41 # 密码加密
42 def get_md5(pwd):
43     md5_obj = hashlib.md5()
44     md5_obj.update(pwd.encode('utf-8'))
45     # 加盐
46     salt = '你个丑比还想盗劳资的信息'
47     md5_obj.update(salt.encode('utf-8'))
48     return md5_obj.hexdigest()
49 
50 
51 # 产生随机的session字符串
52 def get_session():
53     uuid_str = str(uuid.uuid4())
54     session_val = get_md5(uuid_str)
55     return session_val
56 
57 
58 # 登陆认证装饰器
59 def login_auth(func):
60     def inner(*args, **kwargs):   # args = (back_dic, conn)
61         # 校验用户是否登陆:校验session是否一致
62         # back_dic.get('cookies')
63         client_session = args[0].get('cookies')
64         print(client_session)
65         addr = args[0].get('addr')
66         # 上锁:
67         mutex.acquire()
68         # 获取服务端中存储的addr信息
69         session_id_list = user_online.get(addr)
70         # 解锁
71         mutex.release()
72         # 判断是否存在信息
73         if session_id_list:
74             print('测试63',session_id_list[0])
75             # 判断是否相等
76             if client_session == session_id_list[0]:
77                 # 若相等则代表已登录,则将用户id添加到客户端发送过来的字典中
78                 args[0]['user_id'] = session_id_list[1]
79                 # 运行主函数
80                 res = func(*args, **kwargs)
81                 return res
82         else:
83             send_dic = {
84                 'flag': False, 'msg': '携带的session值错误,没有执行权限'
85             }
86             send_data(send_dic, args[1])
87 
88     return inner
common.py
interface文件夹:

 

admin_interface.py
common_interface.py
user_interface

 

db文件夹:
 1 from orm_control.orm import Models
 2 
 3 from orm_control.orm import IntegerField
 4 
 5 from orm_control.orm import StringField
 6 
 7 # 用户表类: id、用户名、密码、用户类型、是否为VIP、注册时间 字段
 8 class User(Models):
 9     u_id = IntegerField(name='u_id', primary_key=True)  # u_id = integerField_obj
10     username = StringField(name='username')
11     password = StringField(name='password')
12     user_type = StringField(name='user_type')  # admin, user
13     # 0: 默认不是vip,   1: 是vip
14     is_vip = IntegerField(name='is_vip')
15     register_time = StringField(name='register_time')
16 
17 
18 # 电影表类: id、电影名字、电影大小、电影md5值、电影是否免费、电影是否删除、上传时间、上传用户的id
19 class Movie(Models):
20     m_id = IntegerField(name='m_id', primary_key=True)
21     movie_name = StringField(name='movie_name')
22     movie_size = StringField(name='movie_size')
23     movie_md5 = StringField(name='movie_md5')
24     # 0: 收费  1:免费
25     is_free = IntegerField(name='is_free')
26     # 0:未删除  1: 已删除
27     is_delete = IntegerField(name='is_delete')
28     upload_time = StringField(name='upload_time')
29     user_id = IntegerField(name='user_id')
30 
31 # 公告表类: id、公告标题, 公告内容、发布时间、发布用户id
32 class Notice(Models):
33     n_id = IntegerField(name='n_id', primary_key=True)
34     title = StringField(name='title')
35     content = StringField(name='content')
36     create_time = StringField(name='create_time')
37     user_id = IntegerField(name='user_id')
38 
39 
40 # 下载记录表: id、下载电影的id、下载用户的id、下载时间
41 class DownloadRecord(Models):
42     d_id = IntegerField(name='d_id', primary_key=True)
43     movie_id = IntegerField(name='movie_id')
44     user_id = IntegerField(name='user_id')
45     download_time = StringField(name='download_time')
models.py
1 user_data.py
2 user_online = {}
user_data

 

conf文件夹:
1 import os
2 
3 BASE_PATH = os.path.dirname(
4     os.path.dirname(__file__)
5 )
6 
7 MOVIE_DIR = os.path.join(BASE_PATH, 'movie_dir')
settings.py
posted @ 2019-12-27 13:28  Mr江  阅读(208)  评论(0编辑  收藏  举报