TCP聊天 传输文件服务器服务器套接字v2.7
刚创建服务器的时候为了后期便于管理, 主要也是MySQL对我不适合, 跨平台使用, 一打包还有得装, 所以直接自己做了个
这是我写的服务器的数据库代码, 可见一看就能看出来, 数据库只存在于单个文件data.json中, I/O十分频繁, 用户信息文件存于运行内存中, 在小数据的情况下速度快, 但到数据存于一定程度, 性能断崖式下跌, 且 在taskmgr(任务管理器)
中内存一举超过了 1.78G的pycharm, 成为第一.
提升性能的方法:
- 变量使用
set
– 可变,无序的,不重复的元素集合 (不可出现list
,set
,bytearray
等无__hash__(self)哈希值的类) – set 的 时间复杂度为 O1, list 的 时间复杂度为 On. – set内部存储元素必是可hash的,而且还是不可重复的. 当每一个set中的元素都有一个独立hash的编码,虽然外面看元素是乱序的,但是内部其实是hash编码的排序,当运行时是通过编码查询,所以会如此之快(warning:是有可能出现hash冲突,但是极少) - 避免单文件频繁调用I/O
- 用户建立文件夹, 一个文件夹对应一个用户的md5值(sha256的都行), 这是为了创建文件夹时候避免非法字符的出现.
- 类似于文件传输服务器, 传来的文件最好解压, 分割切片
- 只将用户名存于运行内存中, 节省空间, 一般数据库也不会大于几TB, 把密码, 注册时间这些杂七杂八的东西放在文件夹下面
行了, 后面的直接不用看了, 按左上角的退出键退出吧
这是data.py代码(全部data.py 在gitcode - https://gitcode.net/m0_60394896/python/-/blob/05267ff4f3c2267954dc87e505d034229eaa0f98/server/data.py)
代码语言:javascript复制from json import load, dump
from os import path, mkdir
from hashlib import md5
from time import time
def encode(data: str):
m = md5()
m.update(data.encode('utf8'))
return m.hexdigest()
file = '.clientsdata.json'
folder = '.clients'
if not path.exists(folder):
mkdir(folder)
class data:
def __init__(self):
if path.exists(file):
with open(file, 'r') as f:
self.data = load(f)
else:
self.data = {}
def __get__(self, username, default=None) -> tuple:
return self.data.get(username, default)
def __in__(self, username) -> bool:
return username in self.data.keys()
def __write__(self) -> None:
with open(file, 'w') as f:
dump(self.data, f, indent=4)
def __register__(self, username, password, time: (int, float) = time()) -> None:
...
self.__write__()
def __login__(self, username, password) -> bool:
return self.data[username][0] == encode(password)
def get_time(self, username):
return self.data[username][1]
def handler(self, type: int, username: str, password: str):
...
文章目录
- 测试
- 提升性能
所有版本记录:
v1.0
: TCP聊天服务器套接字|PyQt5 socket(TCP端口映射 端口放行) logging Thread(含日志,html) anaconda打包32位exe(3.4万字)|python高阶v1.1
: python TCP套接字服务器v1.1-新增服务端命令功能及修改bug(socket PyQt5)v1.2
: python TCP服务器v1.2 - 服务端新增用户登录注册(json, md5加密)v1.3
: python TCP服务器v1.3 - 服务器抗压测试及关闭套接字处理v1.4
: python TCP服务器v1.4 - 客户端连接服务器异常(异常情况分类)处理v1.5
: PyQt5可编辑下拉框(comboBox):editable - python TCP服务器v1.5 - 客户端连接界面增加自定义参数(设置超时, 连接地址可选)v1.6
: Python TCP服务器v1.6 - multiprocessing多进程及Ctrl-c(SIGINT)退出v1.7
: Python TCP服务器v1.7 - PyQt5 server服务端来临v1.8
: python TCP服务器v1.8 - PyQt5登录界面美化 淡入淡出v1.9
: socketTCP协程文件 信息传递 - TCP聊天文件服务器v1.9 - 划时代的版本更新(4.6万字)v2.0
: TCP聊天文件服务器v2.0 - 重大bug修复 PyQt5文件传输可视化v2.1
: TCP聊天文件服务器v2.1 - 服务端线程管理(threading.enumerate)v2.2
: TCP聊天文件服务器v2.2 - 服务端客户端套接字解决分包/粘包问题 - SocketQueue继承以及减少冗余v2.3
: gzip的使用 - TCP聊天文件服务器v2.3 - 文件传输建立缓存制度和.gz的解压缩/压缩解决运行内存过大v2.4
: 网络传输测速 - TCP聊天 传输文件服务器服务器套接字v2.4 - socket协程文件传送测速v2.5
: TCP聊天 传输文件服务器服务器套接字v2.5 - socket测速规范已经gzip的弃用v2.6
: TCP聊天 传输文件服务器服务器套接字v2.6 - 登录注册界面更新 - loading界面应用
测试
增加数据库 用户登录注册的时候还是在 v1.2
,
import itertools
from threading import Thread
def threading(Daemon, name=None, **kwargs):
thread = Thread(**kwargs)
thread.setDaemon(Daemon)
if name:
thread.setName(name)
thread.start()
return thread
data = data()
print(data.handler(0, "tqm", "asdf"))
a = 0
for u in itertools.product("qwertyuiop[]asdfghjkl;'\zxcvbnm,./`12345678900-", repeat=6):
p = "阿斯蒂芬asdf"
a = 1
if a % 400 == 0:
data.handler(1, "".join(u), "".join(p), _show_detail=True)
print(a)
else:
data.handler(1, "".join(u), "".join(p), _show_detail=False)
if a > 1000000:
sys.exit(a)
代码语言:javascript复制2022-06-20 12:49:00,951 - data.py[line:51] - INFO: Execute the function User handle, timeit 0.000
(False, '用户不存在!', '')
2022-06-20 12:49:01,743 - data.py[line:51] - INFO:
Execute the function User handle, timeit 0.002
2022-06-20 19:11:20,572 - data.py[line:145] - INFO: size: 799.9Mb(8319541 bytes)
... ...
2022-06-20 19:14:37,143 - data.py[line:51] - INFO: Execute the function User handle, timeit 2.496
2022-06-20 19:14:37,144 - data.py[line:145] - INFO: size: 800.0Mb(8354830 bytes)
2022-06-20 19:17:45,165 - data.py[line:51] - INFO: Execute the function User handle, timeit 2.531
2022-06-20 19:17:45,165 - data.py[line:145] - INFO: size: 800.0Mb(8385887 bytes)
在运行了长达3, 8400次迭代后, 连一个注册用户都已经超过了秒的单位.
提升性能
set | list |
---|---|
add | append |
from json import load, dump, decoder
from os import path, mkdir, listdir
from hashlib import md5
from time import time
import logging
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
logger = logging.getLogger(__name__)
def _mkdir(_folder):
try:
if not path.exists(_folder):
mkdir(_folder)
return True
except NotImplementedError:
logger.exception("")
return False
def encode(_data: str):
m = md5()
m.update(_data.encode('utf8'))
return m.hexdigest()
def timeit(objname: str, ign=True):
def setup_function(func_name):
def _exec_function(*args, _show_detail=True, **kwargs):
startime = time()
_resp = func_name(*args, **kwargs)
if _show_detail:
logger.info("Execute the function %s%s, timeit %0.3f" % (
objname.title(), "" if ign else f" (at {str(func_name)})", time() - startime))
return _resp
return _exec_function
return setup_function
folder = r'.clients'
_mkdir(folder)
data = set()
if path.isdir(folder):
try:
data = set(listdir(folder))
except decoder.JSONDecodeError:
pass
def __in__(username) -> bool:
return username in data
def register(username, password, register_time: (int, float) = time()) -> None:
global data
data.add(username)
user_path = path.join(folder, encode(username))
print(user_path, _mkdir(user_path))
_mkdir(user_path)
with open(path.join(user_path, "user.json"), "w") as f:
dump({"username": username, "password": password, "register_time": int(register_time)}, f, indent=4)
def login(username, password) -> bool:
with open(path.join(path.join(folder, encode(username)), "user.json"), "r") as f:
_data = load(f)
return _data.get("username", "") == username and _data.get("password", "") == password
def get_time(username):
with open(path.join(path.join(folder, encode(username)), "user.json"), "r") as f:
return load(f).get("register_time")
@timeit("User Handle")
def handler(_type: int, username: str, password: str):
username = username.strip()
if not username:
return False, "未填写用户名!", ""
password = password.strip()
if not password:
return False, "未填写密码!", ""
if not 2 <= len(username) <= 12:
return False, "用户名需在2~12位之间!", ""
if not 4 <= len(password) <= 10:
return False, "密码需在4~10位之间!", ""
if _type == 0: # login
if not __in__(username):
return False, "用户不存在!", ""
if not login(username, password):
return False, "用户名 / 密码错误!", ""
return True, "欢迎回来, " username, username
elif _type == 1: # register
if __in__(username):
return False, "已存在用户!", ""
register(username, password)
return True, "初来乍到, " username, username
就是将每个用户的用户名的md5值存于文件夹, user.json
放信息