根据自定义协议实现socket客户端和服务端的通信:

心跳消息

Request :

长度length:

int

4byte

指令分类msgID:

1byte

心跳消息:0x01

Response:

长度length:

int

4byte

指令分类msgID:

char

1byte

心跳消息:0x01

返回码code:

1byte

char

成功:0

失败:1

注册消息

Request :

长度length:

int

4byte

指令分类msgID:

1byte

注册消息:0x00

Appid:

string

36byte

appid = '996da38d-a7be-4947-aa4c-f8208b5f4ade'


Imei:

string

15byte

imei = '869858030720693'


Response:

长度length:

int

4byte

指令分类msgID:

1byte

注册消息:0x00

返回码code:

1byte

成功:0

失败:1

获取服务端时间消息

Request :

长度length:

int

4byte

指令分类msgID:

1byte

获取服务端时间消息:0x02

Response:

长度length:

int

4byte

指令分类msgID:

1byte

获取服务端时间消息:0x02

返回码code:

1byte

成功:0

失败:1

服务端时间date-time:

可变长

Xxxx-xx-xx xx-xx-xx

Message.py文件:

import struct

# 注册消息ID
MSG_ID_REGISTER = 0x00
# 心跳消息ID
MSG_ID_HEARTBEAT = 0x01
# 时间消息ID
MSG_ID_DATETIME = 0x02



def pack(fmt, *args):
    args_list = list(args)
    for i, arg in enumerate(args_list):
        if isinstance(arg, str):
            args_list[i] = arg.encode('utf-8')
    return struct.pack(fmt, *args_list)


# 组装注册消息
def msg_register(appid, imei):
    return pack('!b36s15s', MSG_ID_REGISTER, appid, imei)


# 组装心跳消息
def msg_heartbeat():
    return pack('!b', MSG_ID_HEARTBEAT)


# 组装时间消息
def msg_datetime():
    return pack('!b', MSG_ID_DATETIME)


 server.py


import time
import socket
import Message
import threading
from struct import unpack

import logging
# 输出到日志文件中
# logging.basicConfig()参数
# filename="日志文件完整路径",filemode="日志输出的方式:a追加,w覆盖 默认是追加"
# format="日志输出的格式" datefmt="输出日期的格式",level=logging.日志输出级别
logging.basicConfig(filename="server.log", level=logging.DEBUG, filemode="w",
                    format="%(asctime)s>%(levelname)s>%(message)s", datefmt="%Y-%m-%d %H:%M:%S")
#logging.debug("输入到log_1.log:这是一个debug提醒")
logging.info("输入到server.log:这是一个info提醒")


# 字节转字符串
def bytes2str(bytes):
    return bytes.decode('utf-8')
 
 
class Server:
    def __init__(self):
        # 服务器ip与端口
        self.server_address = ('127.0.0.1', 9089)
 
    def start(self):
        # 监听端口
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
            sock.bind(self.server_address)
            sock.listen(1024)
            print(f'Server started on {self.server_address}')
            logging.info("Server started on %s",self.server_address)
 
            while True:
                try:
                    client_socket, addr = sock.accept()
                    handler = RequestHandler(client_socket, addr)
                    thread = threading.Thread(target=handler.run, args=())
                    thread.start()
                except Exception as ex:
                    print(ex)
                    logging.info(" %s", ex)
 
 
class RequestHandler:
    def __init__(self, sock, client_address):
        self.sock = sock
        self.client_address = client_address
 
    def run(self):
        try:
            while True:
                struct_bytes = self.sock.recv(4)  # 读取数据长度(4字节)
                data_size, = unpack('!I', struct_bytes)  # 解struct包,转换为数据长度
 
                data = b''  # 已接收的数据
                recv_size = 0  # 接收数据大小
                buff_size = 8192  # 接收缓冲区大小
                while recv_size < data_size:
                    remain_size = data_size - recv_size  # 计算剩余数据长度
                    if remain_size < buff_size:  # 如果剩余数据长度小于缓冲区长度时,设置缓冲区长度为剩余数据长度
                        buff_size = remain_size
                    recv_data = self.sock.recv(buff_size)  # 接收数据
                    data += recv_data  # 数据累加
                    recv_size = len(data)  # 计算已接收的数据长度
 
                if recv_size == data_size:  # 当数据接收完成时,对数据进行解析和处理
                    print(f'recv data from {self.client_address}, data_size:', len(data))
                    #logger.info("{} - times, output: {}".format(2, 'test-info'))
                    #logger.info("%i - times, output: %s", 2, 'test-info')
                    logging.info('recv data from %s, data_size:%s',self.client_address,len(data))

                    msgid, = unpack('!b', data[:1])  # 读取消息ID,根据消息ID区分处理
                    if msgid == Message.MSG_ID_REGISTER:
                        appid = bytes2str(data[1:37])
                        imei = bytes2str(data[37:52])
                        print(f'appid: {appid}, imei: {imei}')
                        logging.info('appid: %s, imei: %s', appid, imei)
                        error_code = 0  # 错误码 0正常 -1错误
                        response = Message.pack('!I2b', 2, msgid, error_code)
                        self.sock.sendall(response)
                        print(f'send data to {self.client_address}, data_size:{appid}{imei}' )
                    elif msgid == Message.MSG_ID_HEARTBEAT:
                        error_code = 0  # 错误码 0正常 -1错误
                        response = Message.pack('!I2b', 2, msgid, error_code)
                        self.sock.sendall(response)
                        print(f'send data to {self.client_address}, data_size:{msgid}{error_code}')
                    elif msgid == Message.MSG_ID_DATETIME:
                        strtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                        str_len = len(strtime)
                        error_code = 0  # 错误码 0正常 -1错误
                        response = Message.pack(f'!I2b{str_len}s', 2 + str_len, msgid, error_code, strtime)
                        self.sock.sendall(response)

                    '''msgheader, = unpack('!2b', data[:2])  # 读取消息ID,根据消息ID区分处理
                    if msgheader == Message.MSG_HEADER:
                        cmd = 0x13
                        error_code = 0 # 错误码 0成功 1失败
                        str_len = len(error_code)
                        #response = Message.pack(f'!2bI1b1b1bI2b', msghead,str_len, Message.MSG_VER,cmd, error_code, Message.MSG_CRC32,Message.MSG_END)
                        #self.sock.sendall(response)'''

        except Exception as ex:
            print(f"{ex} {self.client_address}")
            logging.info(' %s  %s', ex, self.client_address)
 
 
if __name__ == '__main__':
    Server().start()


client.py:

import socket
import struct
import threading
import time
from multiprocessing import JoinableQueue
 
import Message

import logging
# 输出到日志文件中
# logging.basicConfig()参数
# filename="日志文件完整路径",filemode="日志输出的方式:a追加,w覆盖 默认是追加"
# format="日志输出的格式" datefmt="输出日期的格式",level=logging.日志输出级别
logging.basicConfig(filename="client.log", level=logging.DEBUG, filemode="w",
                    format="%(asctime)s>%(levelname)s>%(message)s", datefmt="%Y-%m-%d %H:%M:%S")
#logging.debug("输入到log_1.log:这是一个debug提醒")
logging.info("输入到client.log:这是一个info提醒")


class Client:
    def __init__(self, host, port):
        self.sock = None
        self.writer = None
        self.reader = None
        self.heartbeat_thread = None
        self.server_address = (host, port)

    def connect(self):
        try:
            print(f'Connecting to server{self.server_address}.')
            logging.info("Connecting to server %s", self.server_address)
            # 与服务端建立socket连接
            self.sock = socket.create_connection(self.server_address)

            # 创建并启动网络数据读取线程
            self.reader = ReaderThread(self)
            self.reader.start()

            # 创建并启动网络数据写入线程
            self.writer = WriterThread(self)
            self.writer.start()


            # 启动心跳线程20230412
            if not self.heartbeat_thread:
                self.heartbeat_thread = HeartbeatThread(self)
                self.heartbeat_thread.start()
            print('Connected sucess.')
            logging.info("Connected sucess")

        except Exception as e:
            print(f'Connection refused. {e}')
            logging.info("Connection refused. %s", e)

    def disconnect(self):
        print('Disconnected from server.')
        logging.info("Disconnected from server.")
        if self.reader:
            self.reader.dispose()
            self.reader = None

        if self.writer:
            self.writer.dispose()
            self.writer = None

        self.sock.close()

    # 注册连接
    def register(self):
        appid = '996da38d-a7be-4947-aa4c-f8208b5f4ade'
        imei = '869858030720693'
        self.writer.send(Message.msg_register(appid, imei))

    # 请求日期时间
    def request_datetime(self):
        self.writer.send(Message.msg_datetime())

 
# 心跳线程
class HeartbeatThread(threading.Thread):
    def __init__(self, cli):
        threading.Thread.__init__(self)
        self.cli = cli
 
    def run(self) -> None:
        print("HeartbeatThread  run")
        while True:
            if self.cli.writer:
                self.cli.writer.send(Message.msg_heartbeat())
            else:
                self.cli.connect()
            time.sleep(3)
 
 
# 数据读取线程
class ReaderThread(threading.Thread):
    def __init__(self, cli):
        threading.Thread.__init__(self)
        self.cli = cli
        self.interrupt = False
 
    def run(self) -> None:
        print("ReaderThread  run")
        while not self.interrupt:
            try:
                struct_bytes = self.cli.sock.recv(4)  # 数据总长度
                data_size, = struct.unpack('!I', struct_bytes)  # 解struct包
 
                data = b''  # 已接收的数据
                recv_size = 0  # 接收数据大小
                buff_size = 8192  # 接收缓冲区大小
                while recv_size < data_size:
                    remain_size = data_size - recv_size
                    if remain_size < buff_size:
                        buff_size = remain_size
                    recv_data = self.cli.sock.recv(buff_size)
                    data += recv_data
                    recv_size = len(data)
 
                if recv_size == data_size:
                    #print(f'ThreadID:{self.ident} recv data: {data}, size:{len(data)}')
                    logging.info("recv data: %s, size: %s",data, len(data))
                    msgid, = struct.unpack('!b', data[:1])
                    error_code, = struct.unpack('!b', data[1:2])
                    #print(f'ThreadID:{self.ident}:msgid:{msgid}, error_code:{error_code}')
                    logging.info("msgid:%s, error_code: %s",msgid, error_code)

                    if msgid == Message.MSG_ID_DATETIME:
                        date_time = data[2:].decode('utf-8')
                        print(f'ThreadID:{self.ident}:recv data:  msgid:{msgid}, error_code:{error_code},date_time:{date_time}')
                    elif msgid == Message.MSG_ID_REGISTER:
                        print(f'ThreadID:{self.ident}:recv data:  msgid:{msgid}, error_code:{error_code}')
                    elif msgid == Message.MSG_ID_HEARTBEAT:
                        print(f'ThreadID:{self.ident}:recv data:  msgid:{msgid}, error_code:{error_code}')

                    #match msgid:
                        #case Message.MSG_ID_DATETIME:
                            #date_time = data[2:].decode('utf-8')
                            #print(date_time)
 
            except Exception as e:
                print(e)
                self.cli.disconnect()
                break
 
    def dispose(self):
        self.interrupt = True
 
 
# 数据写入线程
class WriterThread(threading.Thread):
    def __init__(self, cli):
        threading.Thread.__init__(self)
        self.cli = cli
        self.interrupt = False
        self.queue = JoinableQueue(1024)
 
    def run(self) -> None:
        print("WriterThread  run")
        while not self.interrupt:
            try:
                data = self.queue.get()
                self.cli.sock.sendall(data)
            except Exception as e:
                print(e)
                self.cli.disconnect()
                break
 
    # 数据长度(4字节,不包括本身) + 数据(不定长), 大端网络字节序
    def send(self, data):
        if self.queue:
            print(f'ThreadID:{self.ident} send data: {data}, size:{len(data)}')
            logging.info("send data: %s, size:%s", data,len(data))
            data_size = struct.pack('!I', len(data))
            self.queue.put(data_size + data)
 
    def dispose(self):
        self.interrupt = True
        self.queue = None


if __name__ == '__main__':
    client = Client('127.0.0.1', 9089)
    client.connect()
    client.register()
    client.request_datetime()