python paho.mqtt 服务端项目架构

环境:

ubuntu18.04 + python3.7

在公司项目中,需要用到mqtt进行通信,所有开发了下面的架构

优点:

1、很好的进行不同主题和内人的区分

2、线程操作做了互锁,每个消息独立线程处理

3、很方便的提取消息内容

由三个文件组成:dao.py server.py config.py

架构请自行参考,博客为了记录项目实战的代码

server.py

# coding: utf-8
import paho.mqtt.client as mqtt

from config import PROJECT_CODE, HOST, PORT
from dao import ServerDao

from threading import Lock

import random


class Server:
    def __init__(self):
        self.user_list = []
        self.online_user = []
        self.online_player = []
        self.client = mqtt.Client()
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.connect(HOST, PORT, 60)
        self.lock = Lock()
        self.loop_num = 0
        # handle_func的keys()为要订阅的主题列表
        self.handle_func = {'login': self.login, 'register': self.register,
                            'chat': self.chat, 'is_cancel': self.is_cancel,
                            'cancel': self.cancel, 'find_room': self.find_room}

    def start_loop(self):
        # 用线程锁来控制同时仅能一个loop_forever
        if self.loop_num == 0:
            self.lock.acquire()
            print('获得锁!')
            self.loop_num = 1
            self.client._thread_terminate = False
            self.client.loop_forever()

    def stop_loop(self):
        # 停止这个线程
        if self.loop_num == 1:
            self.lock.release()
            print('解锁!!')
            self.client._thread_terminate = True
            self.loop_num = 0

    def on_connect(self, client, userdata, flags, rc):
        if rc == 0:
            print("Connected successfully ")
        for topic in self.handle_func.keys():
            client.subscribe(topic)

    def on_message(self, client, userdata, msg):
        # 规定传入数据均为dict的形式
        data = eval(msg.payload.decode('utf-8'))
        if ServerDao.check_publish_topic(msg.topic, data):
            if msg.topic in self.handle_func.keys():
                func = self.handle_func[msg.topic]
                func(data)

    def cancel(self, data):
        msg = []
        while data["machine_id"] in self.online_user:
            self.online_user.remove(data["machine_id"])
        print("self.online_user = ", self.online_user)
        for room_id, player in enumerate(self.online_player):
            red_id, black_id, is_cancel = player
            if red_id == data["machine_id"] or black_id == data["machine_id"]:
                msg.append([black_id, "", "cancel_ok", "", ""])
                msg.append([red_id, "", "cancel_ok", "", ""])
                ServerDao.publish_cancel_room(msg)
                self.online_player[room_id][-1] = True
                self.online_player.remove(room_id)
                print("self.user_list = ", self.user_list)
                print("self.online_player = ", self.online_player)
                while red_id in self.online_user and black_id in self.online_user:
                    try:
                        self.online_user.remove(red_id)
                        self.online_user.remove(black_id)
                    except:
                        pass

    def is_cancel(self, data):
        is_cancel_room = False
        if data["machine_id"] in self.online_user:
            msg = 'True'
        else:
            msg = 'False'
        print("self.online_user = ", self.online_user)
        print(msg)
        # for room_id, player in enumerate(self.online_player):
        #     red_id, black_id, is_cancel = player
        #     if red_id == data["machine_id"] or black_id == data["machine_id"]:
        #         is_cancel_room = is_cancel
        #         break
        # ServerDao.publish_iscancel_room(str(is_cancel_room))
        ServerDao.publish_iscancel_room(msg)

    def find_room(self, data):
        msg = []
        if len(self.user_list) > 1:
            for i, child_id in enumerate(self.user_list):
                if i % 2 == 0:
                    room_player = [self.user_list[i], self.user_list[i - 1]]
                    black_id = self.user_list[i]
                    red_id = random.choice(room_player)
                    for i_ in room_player:
                        if i_ != red_id:
                            black_id = i_

                    msg.append([black_id, "black", "find_room", "", red_id])
                    msg.append([red_id, "red", "find_room", "", black_id])
                    ServerDao.publish_find_room(msg)
                    remove_room = []
                    for players in self.online_player:
                        if red_id in players and black_id in players:
                            remove_room.append(players)
                    for remove_ in remove_room:
                        self.online_player.remove(remove_)
                    self.online_player.append([red_id, black_id, False])
                    print("self.online_player = ", self.online_player)
                    self.user_list.remove(red_id)
                    self.user_list.remove(black_id)

    def login(self, data):
        pass

    def register(self, data):
        if data['data'] == PROJECT_CODE:
            if data['machine_id'] not in self.user_list and 'GSCHESS' in data['machine_id']:
                self.user_list.append(data['machine_id'])
                self.online_user.append(data['machine_id'])
                print("self.user_list = ", self.user_list)
            msg = 'True'
        else:
            msg = 'False'
        ServerDao.publish_register_msg(self.client, data['machine_id'], msg)

    def chat(self, data):
        pass

    def all_notes(self, data):
        pass


if __name__ == '__main__':
    server = Server()
    server.start_loop()

dao.py

# coding: utf-8

import time
import paho.mqtt.client as mqtt
from config import TOPIC_PARAMS, HOST, PORT


class ServerDao:
    @staticmethod
    def check_publish_topic(topic, data):
        keys = data.keys()
        for key in TOPIC_PARAMS[topic]:
            if key not in keys:
                print('publish is not enough')
                return False
        return True

    @staticmethod
    def publish_login_msg(client, return_topic, user_name, msg, token=None):
        """
        回复给客户端的token
        :param user_name:用户名
        :param msg: 验证情况
        :param token:
        :return:
        """
        # client = mqtt.Client()
        # client.connect(HOST, PORT, 60)
        # user_name可以去掉
        data = {'user_name': user_name, 'login_msg': msg}
        if token:
            all_rooms = ChatRoomsModelDao.get_all_rooms()
            data.update({'token': token, 'all_rooms': all_rooms})
        client.publish(return_topic, str(data).encode(), 1)
        print('publish login_msg to ', return_topic, ' succeed')
        # client.loop()

    @staticmethod
    def publish_register_msg(client, user_name, msg):
        """
        回复给客户端的token
        :param user_name:用户名
        :param msg: 插入情况
        :return:
        """
        # 分情况
        data = {'user_name': user_name, 'register_msg': msg}
        client.publish("register_msg", str(data).encode(), 1)
        print('publish register_msg to ', user_name, ' succeed')

    @staticmethod
    def publish_cancel_room(data_):
        client_ = mqtt.Client()
        client_.connect(HOST, PORT, 60)

        publish_data = {'cancel_msg': data_}
        publish_data1 = {'chat_msg': {'machine_id': '', 'role': '', 'action': 'error', 'time': '', 'data': ''}}
        # client_.publish('GSCHESS01001', str(publish_data).encode(), 1)
        # client_.publish('GSCHESS01002', str(publish_data).encode(), 1)
        for i in range(3):
            client_.publish('cancel_msg', str(publish_data).encode(), 1)
            time.sleep(0.2)
        publish_data = {'error_msg': data_}
        client_.publish('cancel_msg', str(publish_data).encode(), 1)
        print('publish all notes to ', "cancel", ' successfully')
        client_.loop()

    @staticmethod
    def publish_iscancel_room(data_):
        client_ = mqtt.Client()
        client_.connect(HOST, PORT, 60)

        publish_data = {'is_cancel_msg': data_}
        client_.publish('is_cancel_msg', str(publish_data).encode(), 1)
        print('publish all notes to ', "iscancel", ' successfully')
        client_.loop()

    @staticmethod
    def publish_find_room(data_):
        """
        给客户端该房间的所有历史记录
        :param return_topic: 标识一个客户端
        :param room_name:
        :return:
        """
        client = mqtt.Client()
        client.connect(HOST, PORT, 60)

        publish_data = {'find_room_msg': data_}
        for i in range(4):
            client.publish('find_room_msg', str(publish_data).encode(), 1)
            time.sleep(0.15)

        print('publish all notes to ', "find_room", ' successfully')
        client.loop()

    @staticmethod
    def publish_latest_note(return_topic, note_id):
        """
        将最新发布的信息发送给所有订阅的客户端
        :param return_topic: 标识房间
        :param note_id:
        :return:
        """
        client = mqtt.Client()
        client.connect(HOST, PORT, 60)
        model = ChatNotesModelDao.query_one_note(note_id)
        user_name = UserModelDao.get_name_by_user_id(model.user_id)
        data = [user_name, model.message, model.time]
        publish_data = {'latest_note': data}
        client.publish(return_topic, str(publish_data).encode(), 1)
        print('publish one note to ', return_topic, ' successfully')
        client.loop()

    @staticmethod
    def publish_one_step_chess(return_topic, data_):
        """
        将最新发布的信息发送给所有订阅的客户端
        :param return_topic: 标识房间
        :param note_id:
        :return:
        """
        client = mqtt.Client()
        client.connect(HOST, PORT, 60)
        publish_data = {'chess': data_}
        client.publish(return_topic, str(publish_data).encode(), 1)
        print('publish one note to ', return_topic, ' successfully')
        client.loop()

    @staticmethod
    def publish_invalid_msg(client, return_topic):
        """
        回复给客户端的token
        :param client:
        :param return_topic:
        :return:
        """
        # user_name可以去掉
        msg = 'Token invalid '
        data = {'error_msg': msg}
        client.publish(return_topic, str(data).encode(), 1)
        print('publish error_msg to ', return_topic, ' succeed')

config.py

# coding: utf-8

HOST = "1.tcp.cpolar.cn"
# HOST = "192.168.0.246"
PORT = 20029
# PORT = 1883

# 定义topic
# TOPIC = {'登录': 'login', '注册': 'register', '聊天': 'chat'}

# 登录时的token由客户端生成
login_params = ['user_name', 'user_pwd']  # user_room不确定要不要下
register_params = ['machine_id', 'role', 'action', 'time', 'data']
chat_params = ['machine_id', 'role', 'action', 'time', 'data']
all_notes_params = ['machine_id', 'role', 'action', 'time', 'data']
cancel_params = ['machine_id', 'role', 'action', 'time', 'data']
is_cancel_params = ['machine_id', 'role', 'action', 'time', 'data']
find_room_params = ['machine_id', 'role', 'action', 'time', 'data']

TOPIC_PARAMS = {'login': login_params, 'register': register_params,
                'chat': chat_params, 'all_notes': all_notes_params,
                'cancel': cancel_params, 'find_room': find_room_params,'is_cancel': is_cancel_params}
PROJECT_CODE = 'HELLO CHESS'

# 服务端发送给客户端的反馈格式为: topic: user_name, 成功时数据为{'token': 'xxxxx', 'all_rooms': []}
# 服务端发送给客户端一个房间所有数据: topic为房间名,数据为{'all_notes': data},
# data是[user_name, model.message, model.time]
# 服务端发送给客户端最新消息

 

你可能感兴趣的:(linux学习,个人学习记录,python)