资讯详情

TCP聊天文件服务器v2.2 - 服务端客户端套接字解决分包/粘包问题 - SocketQueue继承以及减少冗余

TCP聊天 传输文件服务器服务器套接字v2.2 folder 封面是整个图

所有版本记录: 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板型电阻

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

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