Socket客户端对粘包和数据压缩处理

263 阅读2分钟
# -*- coding: utf-8 -*-

import json
import socket
import struct
import sys
import time
import gzip



class Client(object):

    def __init__(self, host: str, port: int):
        """
        初始化
        Args:
            host: 建立连接的IP
            port: 建立连接的端口
            buffer: 一次读取的字节大小
            timeout: 超时时间
            encoding: 字符编码格式
        """
        self.host = host
        self.port = port
        self.buffer = 512
        self.timeout = 10
        self._encoding = 'utf-8'
        self.tcp_client_socket = None

    def connect(self) -> object:
        """
        服务端应用程序建立连接
        Returns:
        """

        # 创建tcp客户端套接字
        # 1. AF_INET:表示ipv4
        # 2. SOCK_STREAM: tcp传输协议
        try:
            self.tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            self.tcp_client_socket.connect((self.host, self.port))


        except socket.error as e:
            self.tcp_client_socket.close()
            time.sleep(5)
            print("socket连接失败,请确认主机是否开启或防护墙是否关闭", e)
            self.connect()
        return self.tcp_client_socket

    def get_response(self, tcp_client_socket) -> dict:
        """

        Args:
            tcp_client_socket: 连接对象

        Returns:
            返回服务器传输回来的数据,字符类型
        """
        false = False
        true = True
        null = None

        server_dict_data = {}
        try:
            # 先接收服务端固定长度的头信息,这里固定长度为4 Bytes
            header = tcp_client_socket.recv(4)
            # AI等待时间长接收到空字节
            # 判断 是否为空字节,是直接return  header: b''  header_len 0
            if header == b'':
                server_str_data = {}
                print("获取header为空")
                sys.exit()

            # 从头信息中获取接收数据的总量
            header_size = struct.unpack('i', header)[0]
            recv_data = tcp_client_socket.recv(header_size)
            # 解压缩数据
            recv_data = gzip.decompress(recv_data)
            # 转为字符串
            server_str_data = recv_data.decode('utf-8')
            server_str_data = server_str_data.replace('null', 'None').replace('false', 'False').replace('true', 'True')
            server_dict_data = eval(server_str_data)
        except Exception as e:
            sys.exit()
        # 后期在此地方处理id信息
        return server_dict_data

    def send_data(self, send_data: dict) -> bool:
        """
        发送数据
        Args:
            send_data: 发送数据体,默认为bytes格式
        Returns:
            True:发送成功
            False:发送成功
        """
        # 发送socket消息携带包头
        if send_data.get('id') != 9999:
            print(f"C->S:{send_data}")
        send_data = str(send_data)
        msg_bs = send_data.encode("utf-8")
        # 压缩数据体
        msg_bs = gzip.compress(msg_bs)
        msg_struct_len = struct.pack("i", len(msg_bs))
        send_data = msg_struct_len + msg_bs
        # 数据进行解码, 判断是否为字典类型
        try:
            if isinstance(send_data, dict):
                dict_to_json_data = self.dict_to_json(send_data)
                self.tcp_client_socket.send(dict_to_json_data)
            else:
                self.tcp_client_socket.send(send_data)
        except Exception as e:
            return False
        return True

    def close_client(self):
        """
        关闭套接字
        Returns:

        """

        self.tcp_client_socket.close()
        print("socket 已关闭")

    def dict_to_json(self, data: dict):
        """
        python字典转JSON格式
        Args:
            data: 传入python字典

        Returns:
            返回JSON格式数据
        """
        try:
            dict_to_json = json.dumps(data)
            json_data = dict_to_json.encode("utf-8")
        except Exception as e:
            raise e
        return json_data

    def json_to_dict(self, data: json):
        """
        python JSON转字典格式
        Args:
            data: 传入JSON

        Returns:
            返回字典格式数据
        """
        try:
            json_data = json.loads(data)
        except Exception as e:
            raise e
        return json_data

转发请注明出处!感谢!