资讯详情

TCP聊天文件服务器v2.0 - 重大bug修复+PyQt5文件传输可视化

TCP聊天 传输文件服务器服务器套接字v2.0 在这里插入图片描述

所有版本记录: 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万字)

文章目录

  • 服务端data.py 致命bug
  • PyQt5 信号化
  • 服务端Client 改进
  • 服务端 最大接受单位
  • 将字节转换为合适的单位(`Kb`, `Mb`, `Gb` ...)
  • 服务端和客户端 `message_handle`类文件传输 不同点问题
      • 服务端的message_handle类:
      • 客户端的message_handle类:
  • 客户端文件传输可视化
    • listWidget添加布局
    • listWidget添加自定义部件显示不完整, 不想在Item但是,如何在点击信号中确认哪一个? 解决措施
        • 添加自定义部件
        • 显示不全 `Item.setSizeHint(QtCore.QSize(width, height))`
        • 不想在Item但是,如何在点击信号中确认哪一个? 解决措施
    • QProgressBar 未响应等待(即繁忙提示)
            • (怎么调用? `emit`呗)
    • 客户端图片(同目录下)
  • 全部代码
    • 客户端
          • user.pyw
          • ProgressBar.py
    • 服务端
          • server.pyw
          • data.py

__version__ = 2.0 

服务端data.py 致命bug

我测试的时候, 我说为什么只能注册不能登录, 粗心写错了.

    def __login__(self, username, password) -> bool:         return self.data[username][0] == encode(>> username <<) <- 这里! 
    def __login__(self, username, password) -> bool:         return self.data[username][0] == encode(password)

源码直接给你放这里

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 = '.\clients\data.json'
folder = '.\clients'
if not path.exists(folder):
    mkdir(folder)


class user():
    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)) -> None:
        self.data[username] = (encode(password), int(time))
        self.__write__()

    def __login__(self, username, password) -> bool:
        return self.data[username][0] == encode(password)

    def handler(self, username, password) -> bool:
        if self.__in__(username):
            return self.__login__(username, password)
        else:
            self.__register__(username, password, time())
            return True

    def get_time(self, username):
        return self.data[username][1]

PyQt5 信号化

无非就是QtCore.pyqtSignal(*args, **kwargs). 触发就是<pyqtSignal>.emit(*args, **kwargs).

文章里头改动挺多的 , 就不给你一一列出了, 也没有什么意义, PyQt5 不能在其他线程中修改界面线程的ui, 所以运行的时候可能闪退, 无响应 现在可以了, 99.9%不会死机(除内存分配不足外).

服务端Client 改进

    def input(self, text):
        self._send(text)
        return self.recv()

因此受变动的还有login()

    @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)

login ; forever_receive函数, 从计入日志@to_logging转变为了忽略错误@ignore. Client类给你放这了

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, Server):
            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)

服务端 最大接受量单位

为什么老是把最大接受量调到≥1MB还是不行? 原来我自己都忘了自己写的单位忘换算了…

		def retranslateUi(self):
		     self.label_2.setText(_translate("MainWindow", "Maximum load(kb):"))
		     # ...
        @to_logging
        def handle(self):
            self.max_recv = int(self.lineEdit.text()) * 1024   # 单位是kb, 换算为字节.
            # ... 

Iterface界面代码:

class Interface(QtWidgets.QMainWindow):
        Database_signal = QtCore.pyqtSignal(str)
        Usernum_signal = QtCore.pyqtSignal(int)
        def __init__(self):
            super(Interface, self).__init__()
            self.setupUi()
            self.show()
        def setupUi(self):
            self.setObjectName("MainWindow")
            self.resize(1088, 685)
            font = QtGui.QFont()
            font.setFamily("Consolas")
            font.setPointSize(11)
            self.setFont(font)
            self.setStyleSheet("")
            self.centralwidget = QtWidgets.QWidget(self)
            self.centralwidget.setObjectName("centralwidget")
            self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
            self.gridLayout.setObjectName("gridLayout")
            self.label_6 = QtWidgets.QLabel(self.centralwidget)
            self.label_6.setObjectName("label_6")
            self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
            self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_2.setObjectName("textEdit_2")
            self.gridLayout.addWidget(self.textEdit_2, 5, 0, 1, 1)
            self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
            self.groupBox.setObjectName("groupBox")
            self.formLayout_2 = QtWidgets.QFormLayout(self.groupBox)
            self.formLayout_2.setObjectName("formLayout_2")
            self.label_2 = QtWidgets.QLabel(self.groupBox)
            self.label_2.setObjectName("label_2")
            self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
            self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
            self.lineEdit.setObjectName("lineEdit")
            self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit)
            self.label_8 = QtWidgets.QLabel(self.groupBox)
            self.label_8.setObjectName("label_8")
            self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_8)
            self.lineEdit_3 = QtWidgets.QLineEdit(self.groupBox)
            self.lineEdit_3.setObjectName("lineEdit_3")
            self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_3)
            self.label_7 = QtWidgets.QLabel(self.groupBox)
            self.label_7.setObjectName("label_7")
            self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_7)
            self.lineEdit_2 = QtWidgets. 

标签: zb2188板型电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台