一、项目总结三步骤

  1. 项目生命周期为基准线、分析要有层次感、不要想到什么说什么。
  2. 这条基准线上,负责的是哪一块,做了什么。
  3. 举例说明项目中遇到的问题及怎么解决的。

二、项目需求分析

  1. 管理员
  2. 1 注册
  3. 2 登录
  4. 3 上传视频
  5. 4 删除视频
  6. 5 发布公告
  7. 用户
  8. 1 注册
  9. 2 登录
  10. 3 冲会员
  11. 4 查看视频
  12. 5 下载免费视频
  13. 6 下载收费视频
  14. 7 查看观影记录
  15. 8 查看公告

三、搭建框架

  1. 层级结构:客户端 服务端 数据库
  2. 客户端:
  3. 基于tcp连接的套接字程序
  4. 管理员视图
  5. 注册、登录、上传视频、删除视频、发布公告
  6. 用户视图
  7. 注册、登录、购买vip、查看视频、下载免费视频、下载收费视频、查看下载记录、查看公告
  8. 服务端:
  9. tcpserver:基于多线程实现并发的套接字通信 解决粘包问题
  10. interfaceadmin_interfaceuser_interfacecommon_interface
  11. models类和ORM框架:models类中的四张表继承ORM框架中的基类model
  12. 数据库:
  13. 创建四张表:usermovienoticedownload_record

四、ORM框架分析

  1. # 优点:让一个不懂数据库操作的小白也能够简单快速操作数据库实现相应功能
  2. # 缺点:sql封装固定,不利于sql查询优化
  3. # 对象关系映射
  4. # 类 >>> 数据库的表
  5. # 对象 >>> 表的一条条的记录
  6. # 对象获取属性或方法 >>> 记录的字段对应的值
  7. # 一张表有字段,字段又有字段名,字段类型,字段是否是主键,字段的默认值
  8. class Field(object):
  9. pass
  10. # 为了在定义的时候更加方便 通过继承Field定义具体的字段类型
  11. class StringField(Field):
  12. pass
  13. class IntegerField(Field):
  14. pass
  15. class Models(dict):
  16. pass
  17. def __getattr__(self,item):
  18. return self.get(item)
  19. def __setattr__(self,key,value)
  20. self[key] = value
  21. # 查询
  22. def select(self,**kwargs):
  23. # select * from userinfo
  24. # select * from userinfo where id = 1
  25. # 新增
  26. def save(self):
  27. # insert into userinfo(name,password) values('jason','123')
  28. # 修改:是基于已经存在了的数据进行修改操作
  29. def update(self):
  30. # update userinfo set name='jason',password='234' where id = 1
  31. """
  32. (******)
  33. hasattr
  34. getattr
  35. setattr
  36. """
  37. # 元类拦截类的创建过程 使它具备表的特性
  38. class ModelsMetaClass(type):
  39. def __new__(cls,class_name,class_bases,class_attrs):
  40. # 只拦截模型表的创建表
  41. if class_name == 'Models':
  42. return type.__new__(cls,class_name,calss_bases,class_attrs)
  43. table_name = class_attrs.get('table_name',class_name)
  44. primary_key = None
  45. mappings = {}
  46. for k,v in class_attrs.items():
  47. if isinstance(v,Field):
  48. mappings[k] = v
  49. if v.primary:
  50. if primary_key:
  51. raise TypeError('主键重复')
  52. primary_key = v.name
  53. for k in mappings.keys():
  54. class_attrs.pop(k)
  55. if not primary_key:
  56. raise TypeError('必须要有一个主键')
  57. class_attrs['table_name'] = table_name
  58. class_attrs['primary_key'] = primary_key
  59. class_attrs['mappings'] = mappings
  60. return type.__new__(cls,class_name,calss_bases,class_attrs)

五、数据库设计

  1. 表结构定义好以后,数据怎么操作,逻辑代码怎么实现就清晰了
  2. 用户表:包含管理员和普通用户信息 user--->id ,name ,password ,is_locked ,is_vip , user_type,register_time
  3. 电影表:movie---->id,name,, path,is_free, is_delete,user_id,create_time,file_md5
  4. 公告表:notice--->id,title,content,create_time,user_id
  5. 下载记录表:download_record---->id,user_id,movie_id,create_time
  6. create table user(
  7. id int auto_increment primary key,
  8. name varchar(255),
  9. password varchar(255),
  10. is_locked int not null default 0,
  11. is_vip int not null default 0,
  12. user_type varchar(255),
  13. register_time varchar(255)
  14. )engine=Innodb charset='utf8';
  15. create table movie(
  16. id int auto_increment primary key,
  17. name varchar(64),
  18. path varchar(255),
  19. is_free int not null default 1,
  20. is_delete int not null default 0,
  21. create_time varchar(255),
  22. file_md5 varchar(255),
  23. user_id int
  24. )engine=Innodb charset='utf8';
  25. create table notice(
  26. id int auto_increment primary key,
  27. title varchar(255),
  28. content varchar(255),
  29. user_id int,
  30. create_time varchar(255)
  31. )engine=Innodb charset='utf8';
  32. create table download_record(
  33. id int auto_increment primary key,
  34. user_id int,
  35. movie_id int,
  36. create_time varchar(255)
  37. )engine=Innodb charset='utf8';

六、项目中各个功能模块分析

  1. 管理员:
  2. 1、注册功能
  3. 客户端
  4. 1-1、选择每个功能之前都需要都需要需要连接上服务器,即需要一个socket对象,每个函数传一个client
  5. 1-2、密码在传递过程中不能是明文吧,需要加密,可选择hashlibmd5加密,定义一个公共方法咯
  6. 1-3、定义一个发送和接收的公共方法、这里要注意的是在这个方法有一个关键字形参、用于传输文件,默认为None
  7. 1-4、考虑一个问题,发送的字典中要包含哪些数据、对于注册这个问题,包含服务器端用于标识的type的功能类型、
  8. 用户名、密码(要加密)、还有用户类型"user_type"(admin或者user)这里是admin类型
  9. 1-5、接收得到的字典back_dic又包含那些数据,常见的就flagmsg,后续的功能中有返回列表类型的
  10. 服务端
  11. 1-6、首先就是基于多线程实现并发的套接字程序,子线程working函数中会先接收到客户端发来的字典(用到jsonstruct模块)
  12. 1-7、有个问题是有什么便利的方法将接收到的字典recv_dic 和与客户端建立连接的socket对象conn 交给接口层中相应的功能进
  13. 行操作数据库,那就定义一个分发函数dispatchrecv_dic,conn),然后判断recv_dic["type]类型和全局func_dic字典中进行
  14. 比对,去执行与之对应的函数,如果传过来的类型不存在func_dic字典中,那就自定义一个字典back_dic(包含flag和msg数据)
  15. 调用服务端公共发送数据方法返回给客户端
  16. 1-8、咱们不知不觉就来到了服务端注册接口了,意味着可以操作数据库啦,就需要用到ORM框架和db目录中models模块中与表一一对应
  17. 的类、这四个类都是根据事先在数据库中定义好的字段进行创建的,不要写错了,字段和类型。这四个类都继承了ORM框架的基类
  18. modle,所以可是直接点就可以调用ORM框架中基类中方法,select方法是类方法,得到的是一个列表套对象,还有save方法,用于保存
  19. ,还有一个update方法用于更新,那咱们回过头来
  20. 1-9、注册功能拿到的recv_dic中可以拿到注册的用户名,得到用户名后使用user_data = models.User.select(name=name )进行判断要注册的
  21. 用户是否存在,若果存在老规矩back_dic(flag为False,msg为注册失败)返回去,不存在那咋整,还能咋整保存到数据库user表中呗,那
  22. 怎么保存呀,name,password,user_type,is_locked和is_vip都有默认值,register_time注册时间的话写个方法 time.strftime("%Y-%m-%d %X")
  23. 这样不就全搞定了,什么数据都拿到了,那就用models.User()把这些数据搞进去创建得到一个对象,对象调用save方法进行方法就ojbk了,不急还有
  24. 要记得通知客户端,老规矩back_dic字典,调用公共发送方法,注册大功告成
  25. 登录
  26. 客户端
  27. 2-1、在注册功能该项目的总体框架都已经打通了任督二脉,我的乖乖,那登录功能需要考虑一个问题,客户端如果登陆成功,是不是需要标记一下登陆状态
  28. ,老规矩在全局定义一个字典,把返回的字典中一个session存到全局字典cookie中,解决了ojbk,
  29. 2-2、发送字典send_dic中type类型修改为login,密码的话照样发送密文,然后over了
  30. 服务器
  31. 2-3、还记得tcpserver模块中的全局func_dic字典吗?强大的地方来了,刚刚只是写了一个注册的映射接口,现在来了一个login类型,那咋整,就往里加一个
  32. login的映射方法,还可以直接拿到recv_dic和conn,任督二脉打通了就是强,哦还有注册和登录都是管理员和普通用户的公共方法,所以放到common_interface
  33. 中,其实放哪都一样只要能找到就行啦 哈哈
  34. 2-4、你要登陆,逻辑点在哪里,首先我要判断你这货存不存在呀,不存在登陆个屁呀,淡定淡定,哈哈,上面说过select方法得到的是列表,别给老子忘了,列表里面
  35. 放的是一个个对象,models中User类调用select方法根据name=recv_dic["name"]得到user_list,如果user_list存在,那就取零号位就拿到user_obj用户对象
  36. 2-5、拿到user_obj对象点表中的字段属性判断其类型和接收的recv_dic字典中类型和密码是否一致,一致的话便可以得到一个back_dic字典了,老规矩包含flag和msg
  37. 2-6、重点来了,这里可能有带你绕,请无关人员速速离开,要返回的back_dic字典中需要添加一个session添加到字典中,这个session是用户登陆成功之后生成的一个
  38. 随机字符串,咱这里也是用hashlib,这里要保证生成的字符串是唯一的,这里需要加盐,加一个当前cpu执行代码的时间 time.clock()
  39. 2-7、,服务端怎么校验用户的登陆问题,考虑两个问题,第一个问题服务端需要保存session,第二个问题当用户退出之后将该用户对应的键值删除?
  40. 那我们如何判断用户走了,运行到哪一段代码就标记用户走了呢,我们可不可以通过addr就可以定位哪一个用户断开了,找到当前用对应的数据删除,数据保存形式
  41. {‘addr’:[session,user_id]} 将这个东西存在哪里呢,可以放在全局,但我们这里把他存到Tcpsever目录下user_data模块中live_user['addr’']=[session,user_id]
  42. 那问题又来,怎么拿到add,第一种思路给每一个函数都添加addr参数,但是这个addr参数只是login函数用到,其他函数都没用到,这样第一种思路很不合理,第二种思路
  43. 可以通过working中接收到的recv_dic字典添加recv_dic["addr"] = str(addr) 再传给每一个函数,在login函数中user_data.live_user[recv_dic["addr"]] = [session,user_obj.id]
  44. 有考虑一个问题,因为多线程要操作公共数据user_data中的live_user字典,就会出现数据错乱,所以要加锁,那这个锁在那里产生呢?我们要在tcpsever全局中产生mutex = Lock()
  45. 在这里产生,但是不能在这里用,因为会出现循环导入问题,tcpserver导入common_interface,在common_interface中又用到tcpserver中的锁,相互导入就出现循环导入,解决办法,
  46. 将锁保存到user_data中 user_data.mutex = mutex,在login中给user_data.live_user[recv_dic["addr"]] = [session,user_obj.id]加锁,直接导入user_data就可以使用到锁啦
  47. 还没完在tcpserver中 用户退出(try...except.(下面的执行的代码就表示其中一个线程断开)..)就要删除user_data.live_user.pop(str(addr)) ,这里也是公共方法需要
  48. 加锁user_data.mutex.acquire()和user_data.mutex.release()
  49. 2-8、下面的功能都需要先登录才能操作,这里来个装饰器功能:校验客户端发过来的随机字符串,如果有这个随机字符串那就正常执行函数,如果没有返回请先登录的提示,意味着客户端
  50. 发送的字典要带着session过来,装饰器inner(*args,**kwargs)中args=(recv_dic,conn) kwargs={} 拿到客户端发过来的随机字符串与服务器的数据进行比对 vlues=[session,user_id]
  51. for vlues in user_data.live_user.vlues(): if args[0].get("session") == v[0]:将对应的user_id放入recv_dic中,以便后续使用args[0]["user_id"]=vlues[1] break
  52. 以上for循环不一定能找到,for循环只是单单的判断session,然后将user_id放到接收字典recv_dic中,那被装饰的函数到底执不执行,if args[0].get("user_id"): func(*args,**kwargs)
  53. else: back_dic ={"flag"False,"msg":"请先登录"} 然后调用返回函数send_back(back_dic,args[1])
  54. 3、上传视频
  55. 客户端
  56. 3-1、查看有哪些影片需要上传的,即获取所有视频
  57. 3-2、判断影片是否存在才能上传,那应该怎么判断是个问题,我们能不能对上传的视频文件进行hashlib,自定义被hash的数据可以在文件开头,1/3,2/3,末尾-10然后得到md5值
  58. 发送字典类型"check_movie",包含"session","file_md5",得到字典back_dic,如果视频不存在那要输入is_free,是否免费,然后在发字典send_dic,该字典类型为"upload_movie",还包含
  59. "session"、"file_name"、 "file_size"、"file_md5",这里调用公共收发方法是要给文件file传参了,把上传文件路径传过去
  60. 服务端
  61. 3-3、还记得tcpserver模块中的全局func_dic字典吗?加上"check_movie"和"upload_movie"映射,映射函数全都加上装饰器
  62. 3-4、"check_movie"比较简单,只是查看要上传视频的file_md5是否在数据库,注意数据库中存的只是文件地址而已,不是真实的视频文件
  63. 3-5、这里为了避免上传的视频名字是一样的但是内容不一样,所以文件名应该尽量取的唯一,所以给传来的file_name加上一个随机字符串,就直接调用之前定义的 get_session方法即可
  64. 3-6、这里要拼接文件的存放路径了,根据file_size循环写入文件
  65. 3-7、生成一个 movie_obj 电影对象,调用save方法保存,然后返回back_dic说明上传成功
  66. 4、删除视频
  67. 客户端
  68. 4-1、先查询出所有没有被删除的电影列表,即send_dic字典中"type"为'get_movie_list' 和'movie_type'为"all",返回的电影列表可以全部是收费,全部是免费,收费免费都有,这里需要注意的是获取所有视频列表考
  69. 虑的不周全,如果单从管理员角度要获得所有视频不考虑用户获取收费或者免费的视频,会出现一些代码冗余,所以在获取所有视频这个功能要判断传过来的的movie_type是all、free、charge
  70. 4-2、拿到所有视频列表movie_list,该列表的格式[电影名称,是否免费收费,电影id]发送字典send_dic中"type"为"delete_movie"和delete_movie_id'为movie_list[choice-1][2]
  71. 服务端
  72. 4-3、还记得tcpserver模块中的全局func_dic字典吗?加上'get_movie_list'和"delete_movie"映射,映射函数全都加上装饰器
  73. 4-4、删除电影不是真的删除,只是找到每一个电影对象,然后点is_delete属性改为1即可,所以get_movie_list方法会先获得所有对象列表,遍历列表得到每一个对象,对每一个对象的is_delete属性进行判断,注意还要判断
  74. ecv_dic['movie_type'],这里是“all”类型,满足的全部添加到一个返回的列表中back_movie_list,然后返回给客户端
  75. 4-5、delete_movie方法的话 movie_list = models.Movie.select(id=recv_dic.get("delete_movie_id"))然后对列表去索引得到一个电影对象,然后修改movie_obj.is_delete,然后调用update()方法更新,然后返回back_cic
  76. 5、发布公告
  77. 客户端
  78. 5-1 公告包含title和content 发送的字典send_dic包含"type"为"release_notice"、"session"、"title"、"content"
  79. 服务端、
  80. 5-2、这里需要知道接受的字典recv_dic是包含user_id字段的,要写入表notice时用到
  81. 5-3、也是创建表notice对象,然后调用save方法保存
  82. 普通用户
  83. 1、注册
  84. 直接调用公共注册方法
  85. 2、登录
  86. 直接调用公共登录,在全局添加user_dic中保存session和is_vip
  87. 3、购买会员
  88. 客户端
  89. 3-1、判断全局user_dic['is_vip']可知道是否是会员
  90. 3-2、如果不是的话,让用户选择是否购买会员,购买的话最后要修改全局
  91. 服务端
  92. 3-3、根据recv_dic["user_id"]判断是哪一个用户要购买会员,得到的对象点is_vip属性修改为1,调用update(0方法保存
  93. 4、查看所有视频
  94. 客户端
  95. 4-1、发送字典send_dic里面的type为'get_movie_list','movie_type为'all'
  96. 服务器
  97. 4-2、直接调用之前写好的get_movie_list方法即可 这和管理员中删除视频就先获取所有视频
  98. 5、下载免费电影
  99. 客户端
  100. 5-1、先列出所有免费电影,和上个功能差不多,只是'movie_type'改为'free'
  101. 5-2、再发送字典send_dic中'type'为'download_movie' 'movie_id'为movie_list[choice-1][2]
  102. 5-3、接受得到的字典back_dic中有一个wait_time 打印可能是0或者30秒 拼接下载的路径,循环写入文件
  103. 服务端
  104. 5-4、id=recv_dic.get('movie_id')来得到电影列表movie_list,然后索引取值得到电影对象
  105. 5-5 id=recv_dic['user_id']来得到用户列表索引取得用户对象user_obj
  106. 5-6、下载电影的话先判断使用是否是vip,vip的话不需要等待30秒 不是的话需要等待30秒
  107. 5-7、更新下载记录到down_record表中
  108. 5-8、循环发送文件
  109. 5-9、发送字典back_dic
  110. 6、下载收费电影
  111. 客户端
  112. 6-1、针对普通用户和vip用户下载收费视频收费标准不一样(5元 10元)
  113. 6-2、发送字典send_dic 中还是'get_movie_list'但是电影类型为收费'movie_type':'charge'
  114. 6-3、剩下功能和下载免费电影差不多
  115. 服务器
  116. 同上
  117. 7、查看下载记录
  118. 客户端
  119. 7-1、发送字典send_dic 中的类型'check_download_record'
  120. 7-2、接受字典back_dic进行判断即可
  121. 服务端
  122. 7-3、还记得tcpserver模块中的全局func_dic字典吗?加上'check_download_record'的映射方法
  123. 7-4、要查看下载记录 先根据用户id得到一个记录列表,循环该列表得到的是每一个记录对象
  124. 7-5、根据每一个对象点movie_id 和电影id判断得到电影列表,索引取值得到各个对象
  125. 7-6、把每一个对象的名字添加到一个自定义的列表中,用于返回给客户端
  126. 8、查看公告
  127. 客户端
  128. 8-1、发送字典send_dic 中的类型'check_notice'
  129. 8-2、接受字典back_dic进行判断即可
  130. 服务端
  131. 8-3、还记得tcpserver模块中的全局func_dic字典吗?加上'check_notice'的映射方法
  132. 8-4、Notice类调用select方法得到公告列表
  133. 8-5、列表存在的话 遍历该列表得到每一个对象,返回字典中保存对象点title,点content进行返回

七、项目中遇到的问题及怎么解决的

  1. 校验登陆问题(服务端必须校验,客户端无所谓)
  2. 获取所有视频列表考虑的不周全,如果单从管理员角度要获得所有视频不考虑用户获取收费或者免费的视频,会出现一些代码冗余,所以在获取所有视频这个功能要判断传过来的的movie_type是all、free、charge
  3. 服务端怎样标识客户端问题:cookie保存到客户端、session保存到服务器user_data文件中
  4. 从客户端到数据库一顿操作打通以后遇到最多的问题有字段打错了

八、客户端代码框架

8.1 conf

8.1.1 setting

  1. import os
  2. BASE_DIR = os.path.dirname(os.path.dirname(__file__))
  3. UPDATE_MOVIE = os.path.join(BASE_DIR, 'update_movie')
  4. DOWNLOAD_MOVIE_DIR = os.path.join(BASE_DIR, 'download_movie')

8.2 core

8.2.1 src

  1. from core import admin, user
  2. func_dic = {"1": admin.admin_view, "2": user.user_view}
  3. def run():
  4. while True:
  5. print("""
  6. 1、管理员视图
  7. 2、普通用户视图
  8. q、退出
  9. """)
  10. choice = input("please choice your number>>:").strip()
  11. if choice == 'q': break
  12. if choice not in func_dic:
  13. print("choice err not in range")
  14. continue
  15. func_dic.get(choice)()

8.2.2 admin

  1. import os
  2. from Tcpclient import tcpclient
  3. from lib import common
  4. from conf import setting
  5. user_info = {
  6. "session": None
  7. }
  8. def register(client):
  9. while True:
  10. name = input("please input your name>>:").strip()
  11. password = input("please input your password>>:").strip()
  12. re_password = input("please agan input your password>>:").strip()
  13. if password != re_password:
  14. print("两次密码不一致")
  15. continue
  16. send_dic = {"type": "register", "name": name, "password": common.get_md5(password), "user_type": "admin"}
  17. back_dic = common.send_back(send_dic, client)
  18. if back_dic["flag"]:
  19. print(back_dic["msg"])
  20. break
  21. else:
  22. print(back_dic["msg"])
  23. def login(client):
  24. while True:
  25. name = input("please input your name>>:").strip()
  26. password = input("please input your password>>:").strip()
  27. send_dic = {"type": "login", "name": name, "password": common.get_md5(password), "user_type": "admin"}
  28. back_dic = common.send_back(send_dic, client)
  29. if back_dic["flag"]:
  30. print(back_dic["msg"])
  31. user_info["session"] = back_dic["session"]
  32. break
  33. else:
  34. print(back_dic["msg"])
  35. def update_movie(client):
  36. """
  37. 思路:
  38. 1、是不是要先获取有哪些可以上传的视频
  39. 2、选择好要上传的视频后是不是还要判断服务器存不存在,存在了就不需要上传了
  40. 3、那就需要校验视频文件,可自定义校验规则
  41. 4、循环上传
  42. :param client:
  43. :return:
  44. """
  45. while True:
  46. movie_list = common.get_movie()
  47. if not movie_list:
  48. print("暂无影片上传")
  49. return
  50. for i, m in enumerate(movie_list, start=1):
  51. print("%s:%s" % (i, m))
  52. choice = input("please input your choice>>: ").strip()
  53. if choice == 'q': break
  54. if choice.isdigit():
  55. choice = int(choice)
  56. if choice in range(1, len(movie_list) + 1):
  57. file_path = os.path.join(setting.UPDATE_MOVIE, movie_list[choice - 1])
  58. file_md5 = common.get_file_md5(file_path)
  59. send_dic = {"type": "check_movie", "session": user_info["session"], "file_md5": file_md5}
  60. back_dic = common.send_back(send_dic, client)
  61. if back_dic["flag"]:
  62. # 如果可以上传,那标识上传免费还是收费
  63. is_free = input("上传的影片是否免费(y/n)>>:").strip()
  64. is_free = 1 if is_free == 'y' else 0
  65. file_name = movie_list[choice - 1]
  66. send_dic = {"type": "update_movie", "session": user_info["session"], "is_free": is_free,
  67. "file_name": file_name, "file_md5": file_md5, "file_size": os.path.getsize(file_path)}
  68. back_dic = common.send_back(send_dic, client, file_path)
  69. if back_dic["flag"]:
  70. print(back_dic["msg"])
  71. return
  72. else:
  73. print(back_dic["msg"])
  74. else:
  75. print(back_dic["msg"])
  76. else:
  77. print("choice not in range")
  78. else:
  79. print("input choice must be a number !")
  80. def delete_movie(client):
  81. """
  82. 思路:
  83. 1、先从服务器获取所有视频
  84. 2、要删除的发给服务器
  85. :param client:
  86. :return:
  87. """
  88. send_dic = {"type": "get_movie_list", "session": user_info["session"], "movie_type": "all"}
  89. back_dic = common.send_back(send_dic, client)
  90. if back_dic["flag"]:
  91. """
  92. 服务器的get_movie_list会返回一个电影列表,列表里面为[电影名,收费或免费,电影id]
  93. """
  94. movie_list = back_dic["movie_list"]
  95. for i, m in enumerate(movie_list, start=1):
  96. print("%s:%s-%s" % (i, m[0], m[1]))
  97. choice = input("input your delete movie>>:").strip()
  98. if choice.isdigit():
  99. choice = int(choice)
  100. if choice in range(1, len(movie_list) + 1):
  101. send_dic = {"type": "delete_movie", "session": user_info["session"],
  102. "movie_id": movie_list[choice - 1][2]}
  103. back_dic = common.send_back(send_dic, client)
  104. if back_dic['flag']:
  105. print(back_dic['msg'])
  106. return
  107. else:
  108. print(back_dic['msg'])
  109. else:
  110. print('choice noe in range')
  111. else:
  112. print(back_dic['msg'])
  113. def release_notice(client):
  114. while True:
  115. title = input("please input title>>:").strip()
  116. content = input("please input content>>:").strip()
  117. send_dic = {"type": "release_notice", "session": user_info["session"], "title": title, "content": content}
  118. back_dic = common.send_back(send_dic, client)
  119. if back_dic["flag"]:
  120. print(back_dic["msg"])
  121. break
  122. else:
  123. print(back_dic['msg'])
  124. break
  125. func_dic = {
  126. "1": register,
  127. "2": login,
  128. "3": update_movie,
  129. "4": delete_movie,
  130. "5": release_notice
  131. }
  132. def admin_view():
  133. client = tcpclient.get_client()
  134. while True:
  135. print("""
  136. 1、注册
  137. 2、登录
  138. 3、上传电影
  139. 4、删除电影
  140. 5、发布公告
  141. """)
  142. choice = input("please choice your number>>:").strip()
  143. if choice == 'q': break
  144. if choice not in func_dic:
  145. print("choice err not in range")
  146. continue
  147. func_dic.get(choice)(client)

8.2.3 user

  1. import os
  2. import time
  3. from Tcpclient import tcpclient
  4. from conf import setting
  5. from lib import common
  6. user_info = {
  7. "session": None,
  8. "is_vip": None
  9. }
  10. def register(client):
  11. while True:
  12. name = input("please input your name>>:").strip()
  13. password = input("please input your password>>:").strip()
  14. re_password = input("please agan input your password>>:").strip()
  15. if password != re_password:
  16. print("两次密码不一致")
  17. continue
  18. send_dic = {"type": "register", "name": name, "password": common.get_md5(password), "user_type": "user"}
  19. back_dic = common.send_back(send_dic, client)
  20. if back_dic["flag"]:
  21. print(back_dic["msg"])
  22. break
  23. else:
  24. print(back_dic["msg"])
  25. def login(client):
  26. while True:
  27. name = input("please input your name>>:").strip()
  28. password = input("please input your password>>:").strip()
  29. send_dic = {"type": "login", "name": name, "password": common.get_md5(password), "user_type": "user"}
  30. back_dic = common.send_back(send_dic, client)
  31. if back_dic["flag"]:
  32. print(back_dic["msg"])
  33. user_info["session"] = back_dic["session"]
  34. user_info["is_vip"] = back_dic["is_vip"]
  35. break
  36. else:
  37. print(back_dic["msg"])
  38. def buy_vip(client):
  39. while True:
  40. buy_vip = input("是否购买会员(y/n)>>:").strip()
  41. if buy_vip == 'q': break
  42. if buy_vip not in ['y', 'n']:
  43. print("输入有误")
  44. continue
  45. if buy_vip == 'y':
  46. send_dic = {"type": "buy_vip", "session": user_info["session"]}
  47. back_dic = common.send_back(send_dic, client)
  48. if back_dic["flag"]:
  49. print(back_dic["msg"])
  50. break
  51. else:
  52. print(back_dic["msg"])
  53. else:
  54. print("欢迎下次购买")
  55. break
  56. def check_movie(client):
  57. send_dic = {"type": "get_movie_list", "session": user_info["session"], "movie_type": "all"}
  58. back_dic = common.send_back(send_dic, client)
  59. if back_dic["flag"]:
  60. movie_list = back_dic["movie_list"]
  61. for i, m in enumerate(movie_list, start=1):
  62. print("%s:%s-%s" % (i, m[0], m[1]))
  63. else:
  64. print(back_dic["msg"])
  65. def download_free_movie(client):
  66. send_dic = {"type": "get_movie_list", "session": user_info["session"], "movie_type": "free"}
  67. back_dic = common.send_back(send_dic, client)
  68. if back_dic["flag"]:
  69. movie_list = back_dic["movie_list"]
  70. for i, m in enumerate(movie_list, start=1):
  71. print("%s:%s-%s" % (i, m[0], m[1]))
  72. while True:
  73. choice = input("请选择要下载的电影编号>>:").strip()
  74. if choice == 'q': break
  75. if choice.isdigit():
  76. choice = int(choice)
  77. if choice in range(1, len(movie_list) + 1):
  78. send_dic = {"type": "download_movie", "session": user_info["session"],
  79. "movie_id": movie_list[choice - 1][2], "movie_type": "free"}
  80. back_dic = common.send_back(send_dic, client)
  81. if back_dic["flag"]:
  82. print("请等待》》》")
  83. time.sleep(back_dic["wait_time"])
  84. file_path = os.path.join(setting.DOWNLOAD_MOVIE_DIR, back_dic["file_name"])
  85. recv_size = 0
  86. with open(file_path, 'wb') as f:
  87. while recv_size < back_dic["file_size"]:
  88. data = client.recv(1024)
  89. f.write(data)
  90. recv_size += len(data)
  91. print("下载成功")
  92. return
  93. else:
  94. print(back_dic["msg"])
  95. else:
  96. print("choice not in range")
  97. else:
  98. print("choice must be number")
  99. else:
  100. print(back_dic["msg"])
  101. def download_charge_movie(client):
  102. if user_info["is_vip"]:
  103. charge = input("请支付10元(y/n)>>:").strip()
  104. else:
  105. charge = input('请支付20元(y/n)>>:').strip()
  106. if charge != "y":
  107. print("慢走 不送")
  108. return
  109. send_dic = {"type": "get_movie_list", "session": user_info["session"], "movie_type": "charge"}
  110. back_dic = common.send_back(send_dic, client)
  111. if back_dic["flag"]:
  112. movie_list = back_dic["movie_list"]
  113. for i, m in enumerate(movie_list, start=1):
  114. print("%s:%s-%s" % (i, m[0], m[1]))
  115. while True:
  116. choice = input("请选择要下载的电影编号>>:").strip()
  117. if choice == 'q': break
  118. if choice.isdigit():
  119. choice = int(choice)
  120. if choice in range(1, len(movie_list) + 1):
  121. send_dic = {"type": "download_movie", "session": user_info["session"],
  122. "movie_id": movie_list[choice - 1][2], "movie_type": "free"}
  123. back_dic = common.send_back(send_dic, client)
  124. if back_dic["flag"]:
  125. print("请等待》》》")
  126. time.sleep(back_dic["wait_time"])
  127. file_path = os.path.join(setting.DOWNLOAD_MOVIE_DIR, back_dic["file_name"])
  128. recv_size = 0
  129. with open(file_path, 'wb') as f:
  130. while recv_size < back_dic["file_size"]:
  131. data = client.recv(1024)
  132. f.write(data)
  133. recv_size += len(data)
  134. print("下载成功")
  135. return
  136. else:
  137. print(back_dic["msg"])
  138. else:
  139. print("choice not in range")
  140. else:
  141. print("choice must be number")
  142. else:
  143. print(back_dic["msg"])
  144. def download_movie_record(client):
  145. """
  146. 思路:当前登录的用户需要查看自己的观影记录,需要得到电影名
  147. :param client:
  148. :return:
  149. """
  150. send_dic = {"type": "download_movie_record", "session": user_info["session"]}
  151. back_dic = common.send_back(send_dic, client)
  152. if back_dic["flag"]:
  153. back_record_list = back_dic['back_record_list']
  154. for m in back_record_list:
  155. print(m)
  156. else:
  157. print(back_dic['msg'])
  158. def check_notice(client):
  159. """
  160. 查看公告思路:
  161. :param client:
  162. :return:
  163. """
  164. send_dic = {"type": "check_notice", "session": user_info["session"]}
  165. back_dic = common.send_back(send_dic, client)
  166. if back_dic["flag"]:
  167. back_record_list = back_dic['back_notice_list']
  168. for m in back_record_list:
  169. print(m)
  170. else:
  171. print(back_dic['msg'])
  172. func_dic = {
  173. "1": register,
  174. "2": login,
  175. "3": buy_vip,
  176. "4": check_movie,
  177. "5": download_free_movie,
  178. "6": download_charge_movie,
  179. "7": download_movie_record,
  180. "8": check_notice
  181. }
  182. def user_view():
  183. client = tcpclient.get_client()
  184. while True:
  185. print("""
  186. 1、注册
  187. 2、登录
  188. 3、购买会员
  189. 4、查看所有电影
  190. 5、下载免费电影
  191. 6、下载收费电影
  192. 7、查看观影记录
  193. 8、查看公告
  194. """)
  195. choice = input("please choice your number>>:").strip()
  196. if choice == 'q': break
  197. if choice not in func_dic:
  198. print("choice err not in range")
  199. continue
  200. func_dic.get(choice)(client)

8.3 lib

8.3.1 common

  1. import hashlib
  2. import json
  3. import os
  4. import struct
  5. from conf import setting
  6. def send_back(send_dic, client, file=None):
  7. json_bytes = json.dumps(send_dic).encode("utf-8")
  8. client.send(struct.pack('i', len(json_bytes)))
  9. client.send(json_bytes)
  10. if file:
  11. with open(file, 'rb') as f:
  12. for line in f:
  13. client.send(line)
  14. recv_len = struct.unpack('i', client.recv(4))[0]
  15. recv_dic = json.loads(client.recv(recv_len).decode("utf-8"))
  16. return recv_dic
  17. def get_md5(password):
  18. md = hashlib.md5()
  19. md.update(password.encode("utf-8"))
  20. return md.hexdigest()
  21. def get_movie():
  22. movie_list = os.listdir(setting.UPDATE_MOVIE)
  23. return movie_list
  24. def get_file_md5(path):
  25. md = hashlib.md5()
  26. file_size = os.path.getsize(path)
  27. file_list = [0, file_size // 3, (file_size // 3) * 2, file_size - 10]
  28. with open(path, "rb") as f:
  29. for line in file_list:
  30. f.seek(line)
  31. md.update(f.read(10))
  32. return md.hexdigest()

8.4 Tcpclient

8.4.1 tcpclient

  1. import socket
  2. def get_client():
  3. client = socket.socket()
  4. client.connect(("127.0.0.1", 1688))
  5. return client

8.5 start

8.5.1 start

  1. import os, sys
  2. from core import src
  3. sys.path.append(os.path.dirname(__file__))
  4. if __name__ == '__main__':
  5. src.run()

九、服务端框架

9.1 conf

9.1.1 setting

  1. import os
  2. BASE_DIR = os.path.dirname(os.path.dirname(__file__))
  3. MOVIE_DIR = os.path.join(BASE_DIR, 'movie_dir')

9.2 db

9.2.1 models

  1. from orm_pool.orm import Models, StringField, IntegerField
  2. class User(Models):
  3. table_name = 'user'
  4. id = IntegerField("id", primary_key=True)
  5. name = StringField("name")
  6. password = StringField("password")
  7. is_locked = IntegerField("is_locked", default=0)
  8. is_vip = IntegerField("is_vip", default=0)
  9. user_type = StringField("user_type")
  10. register_time = StringField("register_time")
  11. class Movie(Models):
  12. table_name = "movie"
  13. id = IntegerField("id", primary_key=True)
  14. name = StringField("name", column_type="varchar(64)")
  15. path = StringField("path")
  16. is_free = IntegerField("is_free")
  17. is_delete = IntegerField("is_delete", default=0)
  18. create_time = StringField("create_time")
  19. user_id = IntegerField("user_id")
  20. file_md5 = StringField("file_md5")
  21. class Notice(Models):
  22. table_name = "notice"
  23. id = IntegerField("id", primary_key=True)
  24. title = StringField("title")
  25. content = StringField("content")
  26. user_id = IntegerField("user_id")
  27. create_time = StringField("create_time")
  28. class DownloadRecord(Models):
  29. table_name = "download_record"
  30. id = IntegerField("id", primary_key=True)
  31. user_id = IntegerField("user_id")
  32. movie_id = IntegerField("movie_id")
  33. create_time = StringField("create_time")

9.3 interface

9.3.1 admin_interface

  1. import os
  2. from db import models
  3. from lib import common
  4. from conf import setting
  5. @common.login_auth
  6. def check_movie(recv_dic, conn):
  7. movie_data = models.Movie.select(file_md5=recv_dic["file_md5"])
  8. if movie_data:
  9. back_dic = {"flag": False, "msg": "该电影已存在"}
  10. else:
  11. back_dic = {"flag": True, "msg": "可以上传"}
  12. common.send_back(back_dic, conn)
  13. @common.login_auth
  14. def update_movie(recv_dic, conn):
  15. file_name = common.get_session(recv_dic["file_name"]) + recv_dic["file_name"]
  16. file_path = os.path.join(setting.MOVIE_DIR, file_name)
  17. print(recv_dic)
  18. recv_size = 0
  19. with open(file_path, 'wb') as f:
  20. while recv_size < recv_dic["file_size"]:
  21. data = conn.recv(1024)
  22. f.write(data)
  23. recv_size += len(data)
  24. movie_obj = models.Movie(name=file_name, path=file_path, is_free=recv_dic.get("is_free"), is_delete=0,
  25. create_time=common.get_time(), user_id=recv_dic.get("user_id"),
  26. file_md5=recv_dic.get("file_md5"))
  27. movie_obj.save()
  28. back_dic = {"flag": True, "msg": "上传成功"}
  29. common.send_back(back_dic, conn)
  30. @common.login_auth
  31. def delete_movie(recv_dic, conn):
  32. movie_obj = models.Movie.select(id=recv_dic.get('movie_id'))[0]
  33. movie_obj.is_delete = 1
  34. movie_obj.update()
  35. back_dic = {"flag": True, "msg": "删除成功"}
  36. common.send_back(back_dic, conn)
  37. @common.login_auth
  38. def release_notice(recv_dic, conn):
  39. title = recv_dic["title"]
  40. content = recv_dic["content"]
  41. user_id = recv_dic["user_id"]
  42. create_time = common.get_time()
  43. notice_obj = models.Notice(title=title, content=content, user_id=user_id, create_time=create_time)
  44. notice_obj.save()
  45. back_dic = {"flag": True, "msg": "发布成功"}
  46. common.send_back(back_dic, conn)

9.3.2 common_interface

  1. from db import models
  2. from lib import common
  3. from Tcpserver import user_data
  4. def register(recv_dic, conn):
  5. user_list = models.User.select(name=recv_dic["name"])
  6. if user_list:
  7. back_dic = {"flag": False, "msg": "用户已存在"}
  8. common.send_back(back_dic, conn)
  9. return
  10. user_obj = models.User(name=recv_dic["name"], password=recv_dic["password"], is_locked=0, is_vip=0,
  11. user_type=recv_dic["user_type"], register_time=common.get_time())
  12. user_obj.save()
  13. back_dic = {"flag": True, "msg": "注册成功"}
  14. common.send_back(back_dic, conn)
  15. def login(recv_dic, conn):
  16. user_list = models.User.select(name=recv_dic["name"])
  17. if user_list:
  18. user_obj = user_list[0]
  19. if user_obj.user_type == recv_dic["user_type"]:
  20. if user_obj.password == recv_dic["password"]:
  21. back_dic = {"flag": True, "msg": "登陆成功", "is_vip": user_obj.is_vip}
  22. # 获取每个用户的唯一随机字符串,用于标识每个用户
  23. session = common.get_session(user_obj.name)
  24. back_dic["session"] = session
  25. # 服务端要记录正在登录的客户端,将数据user_data文件live_user字典中,放在为了更好的标识
  26. # 每一个客户,字典的key为recv_dic["addr"] -----他是一个元组包含ip和端口,值的话是一个列表
  27. # 保存每一个session和用户id
  28. # 因为时公共数据,且并发会造成数据错乱,咱们给他来个锁
  29. user_data.mutex.acquire()
  30. user_data.live_user[recv_dic["addr"]] = [session, user_obj.id]
  31. user_data.mutex.release()
  32. else:
  33. back_dic = {"flag": False, "msg": "密码不正确"}
  34. else:
  35. back_dic = {"flag": False, "msg": "用户类型不对"}
  36. else:
  37. back_dic = {"flag": False, "msg": "用户不存在"}
  38. common.send_back(back_dic, conn)
  39. @common.login_auth
  40. def get_movie_list(recv_dic, conn):
  41. """
  42. 要给调用者返回相应的电影列表:all、free、charge
  43. :param recv_dic:
  44. :param conn:
  45. :return:
  46. """
  47. movie_list = models.Movie.select()
  48. if movie_list:
  49. back_movie_list = []
  50. for movie_obj in movie_list:
  51. if not movie_obj.is_delete:
  52. if recv_dic["movie_type"] == "all":
  53. back_movie_list.append([movie_obj.name, '免费' if movie_obj.is_free else '收费', movie_obj.id])
  54. elif recv_dic["movie_type"] == "free":
  55. if movie_obj.is_free:
  56. back_movie_list.append([movie_obj.name, '免费', movie_obj.id])
  57. else:
  58. if not movie_obj.is_free:
  59. back_movie_list.append([movie_obj.name, '收费', movie_obj.id])
  60. if back_movie_list:
  61. back_dic = {"flag": True, "movie_list": back_movie_list}
  62. else:
  63. back_dic = {"flag": False, "msg": "暂无影片"}
  64. else:
  65. back_dic = {"flag": False, "msg": "暂无影片"}
  66. common.send_back(back_dic, conn)

9.3.3 user_interface

  1. import os
  2. from conf import setting
  3. from db import models
  4. from lib import common
  5. @common.login_auth
  6. def buy_vip(recv_dic, conn):
  7. user_obj = models.User.select(id=recv_dic['user_id'])[0]
  8. if user_obj.is_vip:
  9. back_dic = {"flag": False, "msg": "您已经是会员啦"}
  10. else:
  11. user_obj.is_vip = 1
  12. user_obj.save()
  13. back_dic = {"flag": True, "msg": "购买成功"}
  14. common.send_back(back_dic, conn)
  15. @common.login_auth
  16. def download_movie(recv_dic, conn):
  17. """
  18. 下载电影功能:普通用户下载需要等待30秒 VIP用下载不需要等待
  19. :param recv_dic:
  20. :param conn:
  21. :return:
  22. """
  23. movie_list = models.Movie.select(id=recv_dic["movie_id"])
  24. if movie_list:
  25. movie_obj = movie_list[0]
  26. file_path = movie_obj.path
  27. user_obj = models.User.select(id=recv_dic["user_id"])[0]
  28. wait_time = 0
  29. if recv_dic["movie_type"] == "free":
  30. if user_obj.is_vip:
  31. wait_time = 0
  32. else:
  33. wait_time = 30
  34. back_dic = {"flag": True, "file_name": movie_obj.name, "file_size": os.path.getsize(file_path),
  35. "wait_time": wait_time}
  36. download_record = models.DownloadRecord(user_id=user_obj.id, movie_id=movie_obj.id,
  37. create_time=common.get_time())
  38. download_record.save()
  39. common.send_back(back_dic, conn)
  40. with open(file_path, 'rb') as f:
  41. for line in f:
  42. conn.send(line)
  43. else:
  44. back_dic = {"flag": False, "msg": "暂无影片"}
  45. common.send_back(back_dic, conn)
  46. @common.login_auth
  47. def download_movie_record(recv_dic, conn):
  48. record_list = models.DownloadRecord.select(user_id=recv_dic["user_id"])
  49. back_record_list = []
  50. if record_list:
  51. for m in record_list:
  52. movie_obj = models.Movie.select(id=m.movie_id)[0]
  53. back_record_list.append(movie_obj.name)
  54. back_dic = {"flag": True, "back_record_list": back_record_list}
  55. else:
  56. back_dic = {"flag": False, "msg": "暂无下载记录"}
  57. common.send_back(back_dic, conn)
  58. @common.login_auth
  59. def check_notice(recv_dic, conn):
  60. notice_list = models.Notice.select()
  61. back_notice_list = []
  62. if notice_list:
  63. for notice_obj in notice_list:
  64. back_notice_list.append([notice_obj.title, notice_obj.content])
  65. back_dic = {"flag": True, "back_notice_list": back_notice_list}
  66. else:
  67. back_dic = {"flag": False, "msg": "暂无下载记录"}
  68. common.send_back(back_dic, conn)

9.4 lib

9.4.1 common

  1. import hashlib
  2. import json
  3. import struct
  4. import time
  5. from functools import wraps
  6. from Tcpserver import user_data
  7. def send_back(back_dic, conn):
  8. json_bytes = json.dumps(back_dic).encode("utf-8")
  9. conn.send(struct.pack('i', len(json_bytes)))
  10. conn.send(json_bytes)
  11. def get_time():
  12. return time.strftime("%Y-%m-%d %X")
  13. def get_session(name):
  14. # 为了保证每个用户的随机字符串是唯一的,不仅要对每一个用户名加密还要加上cpu执行时机----加盐
  15. md = hashlib.md5()
  16. md.update(str(time.clock()).encode("utf-8"))
  17. md.update(name.encode("utf-8"))
  18. return md.hexdigest()
  19. def login_auth(func):
  20. @wraps(func)
  21. def inner(*args, **kwargs):
  22. # args=(recv_dic,conn)
  23. # 登录了以后服务端user_data文件中live_user就有存在用户登陆的数据,如果没有登陆就没有数据,可以为此
  24. # 来作为判断是否登录的依据
  25. for values in user_data.live_user.values():
  26. # values = [session,user_id]
  27. if args[0]["session"] == values[0]:
  28. args[0]["user_id"] = values[1]
  29. break
  30. if args[0].get("user_id"):
  31. func(*args, **kwargs)
  32. else:
  33. back_dic = {"flag": False, "msg": "请先登录"}
  34. send_back(back_dic, args[1])
  35. return inner

9.5 orm_pool

9.5.1 db_pool

  1. from DBUtils.PooledDB import PooledDB
  2. import pymysql
  3. POOL = PooledDB(
  4. creator=pymysql, # 使用链接数据库的模块
  5. maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
  6. mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
  7. maxcached=5, # 链接池中最多闲置的链接,0和None不限制
  8. maxshared=3,
  9. # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
  10. blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
  11. maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
  12. setsession=
  13. [], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
  14. ping=0,
  15. # 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
  16. host='127.0.0.1',
  17. port=3306,
  18. user='root',
  19. password='123',
  20. database='youku',
  21. charset='utf8',
  22. autocommit='True')

9.5.2 mysql_singleton

  1. import pymysql
  2. from orm_pool import db_pool
  3. class Mysql(object):
  4. def __init__(self):
  5. self.conn = db_pool.POOL.connection()
  6. self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
  7. def close(self):
  8. self.cursor.close()
  9. self.conn.close()
  10. def select(self, sql, args=None):
  11. self.cursor.execute(sql, args)
  12. res = self.cursor.fetchall() # 列表套字典
  13. return res
  14. def execute(self, sql, args):
  15. try:
  16. self.cursor.execute(sql, args)
  17. except BaseException as e:
  18. print(e)

9.5.3 orm

  1. from orm_pool.mysql_singleton import Mysql
  2. # 定义字段类
  3. class Field(object):
  4. def __init__(self, name, column_type, primary_key, default):
  5. self.name = name
  6. self.column_type = column_type
  7. self.primary_key = primary_key
  8. self.default = default
  9. # 定义具体的字段
  10. class StringField(Field):
  11. def __init__(self,
  12. name,
  13. column_type='varchar(255)',
  14. primary_key=False,
  15. default=None):
  16. super().__init__(name, column_type, primary_key, default)
  17. class IntegerField(Field):
  18. def __init__(self,
  19. name,
  20. column_type='int',
  21. primary_key=False,
  22. default=None):
  23. super().__init__(name, column_type, primary_key, default)
  24. class ModelMetaClass(type):
  25. def __new__(cls, class_name, class_bases, class_attrs):
  26. # 我仅仅只想拦截模型表的类的创建过程
  27. if class_name == 'Models':
  28. return type.__new__(cls, class_name, class_bases, class_attrs)
  29. # 给类放表名,主键字段,所有字段
  30. table_name = class_attrs.get('table_name', class_name)
  31. # 定义一个存储主键的变量
  32. primary_key = None
  33. # 定义一个字典用来存储用户自定义的表示表的所有字段信息
  34. mappings = {}
  35. # for循环当前类的名称空间
  36. for k, v in class_attrs.items():
  37. if isinstance(v, Field):
  38. mappings[k] = v
  39. if v.primary_key:
  40. if primary_key:
  41. raise TypeError("主键只能有一个")
  42. primary_key = v.name
  43. # 将重复的键值对删除
  44. for k in mappings.keys():
  45. class_attrs.pop(k)
  46. if not primary_key:
  47. raise TypeError('必须要有一个主键')
  48. # 将处理好的数据放入class_attrs中
  49. class_attrs['table_name'] = table_name
  50. class_attrs['primary_key'] = primary_key
  51. class_attrs['mappings'] = mappings
  52. return type.__new__(cls, class_name, class_bases, class_attrs)
  53. class Models(dict, metaclass=ModelMetaClass):
  54. def __init__(self, **kwargs):
  55. super().__init__(**kwargs)
  56. def __getattr__(self, item):
  57. return self.get(item, '没有该键值对')
  58. def __setattr__(self, key, value):
  59. self[key] = value
  60. # 查询方法
  61. @classmethod
  62. def select(cls, **kwargs):
  63. ms = Mysql()
  64. # select * from userinfo
  65. if not kwargs:
  66. sql = 'select * from %s' % cls.table_name
  67. res = ms.select(sql)
  68. else:
  69. # select * from userinfo where id = 1
  70. k = list(kwargs.keys())[0]
  71. v = kwargs.get(k)
  72. sql = 'select * from %s where %s=?' % (cls.table_name, k)
  73. # select * from userinfo where id = ?
  74. sql = sql.replace('?',
  75. '%s') # select * from userinfo where id = %s
  76. res = ms.select(sql, v)
  77. if res:
  78. return [cls(**r) for r in res] # 将数据库的一条数据映射成类的对象
  79. # 新增方法
  80. def save(self):
  81. ms = Mysql()
  82. # insert into userinfo(name,password) values('jason','123')
  83. # insert into %s(%s) values(?)
  84. fields = [] # [name,password]
  85. values = []
  86. args = []
  87. for k, v in self.mappings.items():
  88. if not v.primary_key: # 将id字段去除 因为新增一条数据 id是自动递增的不需要你传
  89. fields.append(v.name)
  90. args.append('?')
  91. values.append(getattr(self, v.name))
  92. # insert into userinfo(name,password) values(?,?)
  93. sql = "insert into %s(%s) values(%s)" % (
  94. self.table_name, ','.join(fields), ','.join(args))
  95. # insert into userinfo(name,password) values(?,?)
  96. sql = sql.replace('?', '%s')
  97. ms.execute(sql, values)
  98. # 修改方法:基于已经存在了的数据进行一个修改操作
  99. def update(self):
  100. ms = Mysql()
  101. # update userinfo set name='jason',password='123' where id = 1
  102. fields = [] # [name,password]
  103. values = []
  104. pr = None
  105. for k, v in self.mappings.items():
  106. if v.primary_key:
  107. pr = getattr(self, v.name, v.default)
  108. else:
  109. fields.append(v.name + '=?')
  110. values.append(getattr(self, v.name, v.default))
  111. sql = 'update %s set %s where %s = %s' % (
  112. self.table_name, ','.join(fields), self.primary_key, pr)
  113. # update userinfo set name='?',password='?' where id = 1
  114. sql = sql.replace('?', '%s')
  115. ms.execute(sql, values)
  116. # if __name__ == '__main__':
  117. # class Teacher(Models):
  118. # table_name = 'teacher'
  119. # tid = IntegerField(name='tid',primary_key=True)
  120. # tname = StringField(name='tname')
  121. # obj = Teacher(tname='jason老师')
  122. # obj.save()
  123. # res = Teacher.select()
  124. # for r in res:
  125. # print(r.tname)
  126. # print(res)
  127. # res = Teacher.select(tid=1)
  128. # teacher_obj = res[0]
  129. # teacher_obj.tname = 'jason老师'
  130. # teacher_obj.update()
  131. # res1 = Teacher.select()
  132. # print(res1)
  133. # class User(Models):
  134. # table_name = 'User'
  135. # id = IntegerField(name='id', primary_key=True)
  136. # name = StringField(name='name')
  137. # password = StringField(name='password')
  138. # print(User.primary_key)
  139. # print(User.mappings)
  140. # obj = User(name='jason')
  141. # print(obj.table_name)
  142. # print(obj.primary_key)
  143. # print(obj.mappings)

9.6 Tcpserver

9.6.1 tcpserver

  1. import json
  2. import socket
  3. import struct
  4. import traceback
  5. from concurrent.futures import ThreadPoolExecutor
  6. from threading import Lock
  7. from Tcpserver import user_data
  8. from interface import common_interface, admin_interface, user_interface
  9. from lib import common
  10. pool = ThreadPoolExecutor(20)
  11. #全局产生锁,为了避免循环导入问题,将产生的锁放到user_data中
  12. mutex = Lock()
  13. user_data.mutex = mutex
  14. func_dic = {
  15. "register": common_interface.register,
  16. "login": common_interface.login,
  17. "check_movie": admin_interface.check_movie,
  18. "update_movie": admin_interface.update_movie,
  19. "get_movie_list": common_interface.get_movie_list,
  20. "delete_movie": admin_interface.delete_movie,
  21. "release_notice": admin_interface.release_notice,
  22. "buy_vip": user_interface.buy_vip,
  23. "download_movie": user_interface.download_movie,
  24. "download_movie_record": user_interface.download_movie_record,
  25. "check_notice": user_interface.check_notice
  26. }
  27. def get_server():
  28. server = socket.socket()
  29. server.bind(("127.0.0.1", 1688))
  30. server.listen(5)
  31. while True:
  32. conn, addr = server.accept()
  33. pool.submit(working, conn, addr)
  34. def working(conn, addr):
  35. while True:
  36. try:
  37. recv_header = conn.recv(4)
  38. recv_bytes = conn.recv(struct.unpack('i', recv_header)[0])
  39. recv_dic = json.loads(recv_bytes.decode("utf-8"))
  40. recv_dic["addr"] = str(addr)
  41. dispatch(recv_dic, conn)
  42. except Exception as e:
  43. traceback.print_exc()
  44. conn.close()
  45. # 当用户断开以后,服务器就无需保存表示他的数据
  46. user_data.mutex.acquire()
  47. user_data.live_user.pop(str(addr))
  48. user_data.mutex.release()
  49. break
  50. def dispatch(recv_dic, conn):
  51. if recv_dic.get("type") in func_dic:
  52. func_dic.get(recv_dic["type"])(recv_dic, conn)
  53. else:
  54. back_dic = {"flag": False, "msg": "类型不合法"}
  55. common.send_back(back_dic, conn)

9.6.2 user_data

live_user={}

mutex=None

9.7 start

9.7.1 start

  1. import os, sys
  2. from Tcpserver import tcpserver
  3. sys.path.append(os.path.dirname(__file__))
  4. if __name__ == '__main__':
  5. tcpserver.get_server()

MySQL数据库实战之优酷的更多相关文章

  1. mysql数据库实战之优酷项目

    管理员: 1.注册功能 客户端 1-1.选择每个功能之前都需要都需要需要连接上服务器,即需要一个socket对象,每个函数传一个client 1-2.密码在传递过程中不能是明文吧,需要加密,可选择ha ...

  2. mysql数据库性能调优总结积累

    mysql数据库的调优大概可以分为四大块 0 架构调优 ---根据业务 读写分库分表   ---主从 读写分离 1 配置的调优 ---开启缓存查询  设置缓存大小 ---最大连接数设置 ---数据库引 ...

  3. Python3连接MySQL数据库实战

    Python3连接MySQL数据库实战 第三方库 :pymysql 数据库连接 def connect(): try: #建立数据库连接,从左至右参数依次为 # ip地址 我用的是云端数据库 如果为本 ...

  4. MySQL 数据库性能调优

    MySQL 数据库性能调优 MySQL性能 最大数据量 最大并发数 优化的范围有哪些 存储.主机和操作系统方面: 应用程序方面: 数据库优化方面: 优化维度 数据库优化维度有四个: 优化选择: 数据库 ...

  5. mysql数据库索引调优

    一.mysql索引 1.磁盘文件结构 innodb引擎:frm格式文件存储表结构,ibd格式文件存储索引和数据. MyISAM引擎:frm格式文件存储表结构,MYI格式文件存储索引,MYD格式文件存储 ...

  6. MySQL 数据库规范--调优篇(终结篇)

    前言 这篇是MySQL 数据库规范的最后一篇--调优篇,旨在提供我们发现系统性能变弱.MySQL系统参数调优,SQL脚本出现问题的精准定位与调优方法. 目录 1.MySQL 调优金字塔理论 2.MyS ...

  7. mysql 数据库缓存调优之解决The total number of locks exceeds the lock table size错误

    环境: mysql5.6.2  主从同步(备注:需操作主库和从库) 一.InnoDB表执行大批量数据的更新,插入,删除操作时会出现这个问题,需要调整InnoDB全局的innodb_buffer_poo ...

  8. MYSQL数据库性能调优之八:mysql日志

    MySQL日志 主要包含:错误日志.查询日志.慢查询日志.事务日志.二进制日志.中继日志: 使用 SHOW GLOBAL VARIABLES LIKE '%log%';  查询所有日志配置详情: 一. ...

  9. MYSQL数据库性能调优之一:调优技术基础

    1.mysql数据库优化技术有哪些? 2.数据库三层结构? 3.数据库3NF

随机推荐

  1. 修改vscode的文件,对应的磁盘文件不改变

    两种解决办法: 首先:修改VSCode默认配置文件,点击左下角设置标志图 -> 设置,出来了设置相关的东西,搜索 files.autoSave 第一种:把"files.autoSave ...

  2. Flutter学习笔记(26)--返回拦截WillPopScope,实现1秒内点击两次返回按钮退出程序

    如需转载,请注明出处:Flutter学习笔记(26)--返回拦截WillPopScope,实现1秒内点击两次返回按钮退出程序 在实际开发中,为了防止用户误触返回按钮导致程序退出,通常会设置为在1秒内连 ...

  3. 编译原理之DFA最小化,语法分析初步

    1.将DFA最小化: 状态转换图: 识别语言:b*ac*(da)*bb* 2.构造以下文法相应的最小的DFA S→ 0A|1B A→ 1S|1 B→0S|0 (1)正规式: S -> 0(1S+ ...

  4. MongoDB 中聚合统计计算--$SUM表达式

    我们一般通过表达式$sum来计算总和.因为MongoDB的文档有数组字段,所以可以简单的将计算总和分成两种:1,统计符合条件的所有文档的某个字段的总和:2,统计每个文档的数组字段里面的各个数据值的和. ...

  5. log file switch (checkpoint incomplete) - 容易被误诊的event

    本文转自 https://blogs.oracle.com/database4cn/log-file-switch-checkpoint-incomplete-%e5%ae%b9%e6%98%93%e ...

  6. 08. Go 语言包(package)

    Go 语言包(package) Go 语言的源码复用建立在包(package)基础之上.Go 语言的入口 main() 函数所在的包(package)叫 main,main 包想要引用别的代码,必须同 ...

  7. enter键的使用

    vue全部的按键别名: .enter .tab .delete (捕获“删除”和“退格”键) .esc .space .up .down .left .right vue中 <input v-o ...

  8. idea 出现Can't load AMD 64-bit .dll on a IA 32-bit platform错误

    这个错误是在说无法在IA 32位平台上加载AMD 64位.dll,解决方法如下 下载安装64位的jdk(方法和安装32位的一致,可百度查阅相关资料) 按如下步骤配置

  9. mpvue快速入门

    主要特性 使用 mpvue 开发小程序,你将在小程序技术体系的基础上获取到这样一些能力: 彻底的组件化开发能力:提高代码复用性 完整的 Vue.js 开发体验 方便的 Vuex 数据管理方案:方便构建 ...

  10. ubuntu 中安装jenkins,基于docker运行jenkins

     本文是在ubuntu环境下安装jenkins,jenkins运行在docker容器中,至于docker如何安装,本文不再描述,大家可以上网查询下,如何安装docker,下面先放上一个使用jenkin ...