TCP聊天 传输文件服务器服务器套接字v2.2
封面是整个图
所有版本记录:
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
: PyQt下拉框可编辑(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)
文章目录
- SocketQueue
-
- Server服务端
-
- 原先:
- 现在:
-
-
-
- socket_queue.py
- server.py Client类的继承
-
-
- User客户端
-
- 原先
- 现在
-
- 继承关系 SocketQueue -> SocketClient -> Socket
-
-
- socket_queue.py
- user.pyw
-
- 图片搬家
- 全部代码
-
- 服务端
-
- data.py
- server.pyw
- socket_queue.py
- 客户端
-
- user.pyw
- socket_queue.py
- file.py
- ProgressBar.py
- 资源(0积分)
SocketQueue
python 完美解决分隔列队机制TCP粘包\分包问题 - csdn - zmh_program的博文 我的文章已经讲述了一般算法,不再重复太多, 主要是继承问题
Server服务端
原先:
class Client(object): class QuitError(Exception): def __init__(self, *args): super().__init__(*args) def __init__(self, socket, addr, server: Server): self.socket = socket self.addr = addr if not isinstance(server, Servr):
raise ValueError
self.server = server
self.encode = self.server.encode
self.max_count = self.server.max_count
self.com = Command_Handler(self)
@self.error
def _recv(self) -> bytes:
return self.socket.recv(self.max_count).decode(encoding=self.encode).strip()
self._recv = lambda: _recv(self)
@self.error
def _send(self, message=None) -> None:
if message:
if isinstance(message,str):
message = repr((normal_text, message)).encode(self.encode)
message += b'\n' #防止粘包
self.socket.sendall(message)
self._send = lambda m: _send(self, m)
def __del__(self):
self.socket.close()
def isLogin(self) -> bool:
return hasattr(self, "_login") and self._login
def isOpen(self) -> bool:
return not getattr(self.socket, "_closed", True)
def __filter__(self) -> bool:
"""返回是否在线并已可接受消息"""
return self.isLogin() and self.isOpen()
def recv(self) -> str:
if not hasattr(self, "queue"): self.queue = [] #列队机制
if self.queue:
return self.queue.pop(0)
while True:
result = list(self.server.user_handle.handle(self._recv(), self))
if result:
self.queue.extend(result)
return self.queue.pop(0)
def input(self, text):
self._send(text)
return self.recv()
@ignore
def login(self):
self.username = self.recv()[:15]
if self.server.user_record.__in__(self.username):
if self.server.user_record.handler(self.username, self.input("<font color='red'>请输入您的密码: (右下[send]键发送)</font>")):
self._send(f'<font color="green">欢迎回来, {
self.username}.</font>')
else:
self._send('<font color="red">密码错误,请重试.</font>')
self.__del__() # = del self
else:
def normal(string):
return (4 <= len(string) <= 10) and not ('\n' in string)
while True:
p1 = self.input("<font color='blue'>[i]提示: 密码需在4 ~ 10位之间, 且不能换行.</font>\n<font color='red'>请输入您的密码: (右下[send]键发送)</font>")
if normal(p1):
break
while True:
p2 = self.input("<font color='red'>再次输入您的密码: (右下[send]键发送)</font>")
if p1 == p2:
break
else:
self._send("<font color='red'>密码与前次不符!</font>")
self.server.user_record.handler(self.username, p1)
self._send(f'初来乍到, {
self.username}')
self._login = True
self.server.login(self.username, self.addr)
def quit(self) -> None:
if hasattr(self, 'Quitted'):
return
self.Quitted = True
if self.isOpen() is True:
self.socket.close()
self.server.quit(self.username, self.addr)
@ignore
def forever_receive(self):
self.login()
while self.__filter__():
string = self.recv()
if string is None:
continue
elif string == Client.QuitError:
return
elif self.com.iscommand(string):
self._send(self.com.handler(string))
else:
self.server.UserMessage(self.addr, self.username, string)
def error(self, func):
def function(*args, **kwargs):
try:
res = func(*args, **kwargs)
return res
except (ConnectionAbortedError,ConnectionRefusedError,ConnectionResetError, OSError) as e:
self.quit()
except Exception:
logger.exception("error")
return Client.QuitError
return function
def run(self):
self.thread = threading(True,name=f"客户端{
self.addr}",target=self.forever_receive)
现在:
socket_queue.py
import socket
def ignore(function):
def func(*args, **kwargs):
try:
return function(*args, **kwargs)
except:
pass
return func
class SocketQueue:
split_text = "\n" # 类变量, 默认分隔符为回车(\n)
class QuitError(ConnectionError):
pass
def __init__(self, socket=socket.socket(), bufsize=1024, codec="utf8"):
self.socket, self.bufsize, self.codec = socket, bufsize, codec
self.waitKey = str()
self.ReadyQueue = []
self._closed = False
def __close__(self):
self.quit()
def __del__(self):
self.quit()
def isOpen(self) -> bool:
return not (self._closed and getattr(self.socket, "_closed", False))
def quitEvent(self) -> None:
pass
def quit(self) -> None:
if not self._closed:
self._closed = True
self.quitEvent()
self.socket.close()
def normal_text(self, string: str):
return string.encode(self.codec)
def __recv(self) -> (bytes, ConnectionError):
try:
data = self.socket.recv(self.bufsize).strip(b" ") # str.strip()不可用! 会将\n省略
if data:
self.parse_data(self.handle(data))
except (ConnectionAbortedError, ConnectionRefusedError, ConnectionResetError, OSError) as e:
self.quit()
return self.QuitError
def __send(self, data: bytes) -> bool:
try:
self.socket.sendall(data)
return True
except (ConnectionAbortedError, ConnectionRefusedError, ConnectionResetError, OSError) as e:
self.quit()
return False
def send(self, data) -> bool:
if isinstance(data, str):
data = self.normal_text(data)
elif isinstance(data, (set, list, tuple)):
data = repr(data)
elif isinstance(data, (int, float)):
data = str(data).encode(self.codec)
elif isinstance(data, bytes):
pass
else:
data = bytes(data)
return self.__send(data + self.split_text.encode(self.codec))
def input(self, text):
self.send(text)
return self.recv()
def forever_receive(self) -> (str, None):
while self.isOpen():
self.recv()
def handle(self, data: bytes):
return [d.strip() for d in data.decode(self.codec).split(self.split_text)]
@ignore
def parse_data(self, generator: (tuple, list, set)) -> None:
generator = list(generator)
if len(generator) == 1: # 列表为1, 表明无间隔符, 则在等待中添加.
self.waitKey += generator[0]
return
self.ReadyQueue.append(self.waitKey + generator.pop(0))
self.waitKey = generator.pop()
self.ReadyQueue.extend(generator)
def recv(self) -> (str, Exception):
while True:
while not self.ReadyQueue:
self.__recv()
if not self.isOpen():
return self.QuitError
data = self.parse_argument(self.ReadyQueue.pop(0))
if isinstance(data, str) and data:
return data
def parse_argument(self, arg: str) -> str:
return arg.strip()
def recv_list(self) -> list:
queue = self.ReadyQueue[:]
self.ReadyQueue = []
return queue
@ignore
def connect(self, host: str, port: int):
assert 0 <= port <= (2 ** 16) - 1
self.socket.connect((host, port))
server.py Client类的继承
class Client(SocketQueue): def __init__(self, socket, addr, server: Server): super(Client, self).__init__(socket, server.max_count, server.encode) self.addr = addr if not isinstance(server, Server): raise ValueError self.server = server self.username = str() self.com = Command_Handler(self) self.thread = threading(True, name=f"客户端{ self.addr}", target=self.forever_receive) self._login = False def normal_text(self, s): return repr((normal_text, s)).encode(self.codec) def isLogin(self) -> bool: return getattr(self, "_login", False) def __filter__(self) -> bool: """返回是否在线并已可接受消息""" return self.isLogin() and self.isOpen() @ignore def login(self): self.username = self.recv()[:15] if self.server.user_record.__in__(self.username): if self.server.user_record.handler(self.username, self.input("<font color='red'>请输入您的密码: (右下[send]键发送)</font>")): self.send(f'<font color="green">欢迎回来, { self.username}.</font>') else: self.send('<font color="red">密码错误,请重试.</font>') self.__del__() else: while True: p1 = self.input( "<font color='blue'>[i]提示: 密码需在4 ~ 10位之间, 且不能换行.</font>\n<font color='red'>请输入您的密码: (右下[send]键发送)</font>") if (4 <= len(p1) <= 10) and not ('\n' in p1): break while True: p2 = self.input("<font color='red'>再次输入您的密码: (右下[send]键发送)</font>") if p1 == p2: break else: self.send("<font color='red'>密码与前次不符!</font>") self.server.user_record.handler(self.username, p1) self.send(f'初来乍到, { self.username}') self._login 标签:
zb2188板型电阻