python-websocket开发(二)网络编程

    上一篇博客介绍了python websocket的协议解析,这一篇介绍网络编程,如何用socket接收发原始数据,
    通过上一篇文章的协议解析,形成程序员能够理解的数据,照例先贴代码,再解释。
from socket import *
import selectors
from sdk.web_socket.protocol_handler import WebSocketProtocolHandler
import multiprocessing
import json
import os
from sdk.web_socket.base_interface import BaseInterface
import time


class WebSocketServer:
    def __init__(self, port=8000):
        self.__server_fileobj = socket(AF_INET, SOCK_STREAM)
        self.__server_fileobj.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        self.__server_fileobj.bind(('0.0.0.0', port))
        self.__server_fileobj.listen(5)
        self.__server_fileobj.setblocking(False)  # 设置socket的接口为非阻塞
        print(f"==========WebSocket bind:{port}==========")

        # socket mgr
        self.__conn_mgr = BaseInterface.create_ipc_dict()
        self.__protocol_handler_mgr = BaseInterface.create_ipc_dict()
        self.__heartcheck_mgr = BaseInterface.create_ipc_dict()

        # multiprocess mgr
        self.__ipc_queue = BaseInterface.create_ipc_queue()

        self.connect_callback = BaseInterface.create_ipc_list()
        self.disconnect_callback = BaseInterface.create_ipc_list()

    def register_connect_callback(self, ref):
        self.connect_callback.append(ref)

    def register_disconnect_callback(self, ref):
        self.disconnect_callback.append(ref)

    def start(self, msg_handler=None):
        receive_process = multiprocessing.Process(name="Receive", target=WebSocketServer.read_main_loop, args=(self, msg_handler, BaseInterface.ipc_func_list, BaseInterface.ipc_param_list))
        receive_process.start()

        send_process = multiprocessing.Process(name="Send", target=WebSocketServer.write_main_loop, args=(self, ))
        send_process.start()

    def push_msg(self, fd, msg_type, msg_body):
        try:
            msg_dict = {
                "msg_type": msg_type,
                "msg_body": msg_body
            }
            msg = json.dumps(msg_dict).encode("utf-8")
            exclusive_dict = {
                "fd": fd,
                "msg": msg
            }
            self.__ipc_queue.put(exclusive_dict)
        except Exception:
            print("can't push this msg_type and msg_body")

    def broadcast(self, msg_type, msg_body):
        try:
            for k in self.__conn_mgr.keys():
                self.push_msg(k, msg_type, msg_body)
        except Exception:
            print("broadcast may not complete.")

    @staticmethod
    def accept(server, server_fileobj, sel, mask, msg_handler, fd_conn_mgr):
        # print("accept")
        conn, addr = server_fileobj.accept()
        sel.register(conn, selectors.EVENT_READ, WebSocketServer.read)
        server.__add_conn(conn)
        fd_conn_mgr[conn.fileno()] = conn

    @staticmethod
    def read(server, conn, sel, mask, msg_handler, fd_conn_mgr):
        try:
            handler = server.__get_protocol_handler(conn.fileno())
            if handler:
                body = handler.recv_msg()

                if not body:
                    sel.unregister(conn)
                    server.__remove_conn(conn)
                    conn.close()
                    if conn.fileno() in fd_conn_mgr.keys():
                        del fd_conn_mgr[conn.fileno()]
                else:
                    if msg_handler is not None:
                        msg_handler.handle_msg(server, body, conn.fileno())
        except Exception as e:
            print(e)
            sel.unregister(conn)
            server.__remove_conn(conn)
            conn.close()
            if conn.fileno() in fd_conn_mgr.keys():
                del fd_conn_mgr[conn.fileno()]

    @staticmethod
    def process_info(info=""):
        print(f"=====module name: {info}=====\nparent process: {os.getppid()}\nprocess id: {os.getpid()}")

    @staticmethod
    def read_main_loop(server, msg_handler, func_list, param_list):
        BaseInterface.ipc_func_list = func_list
        BaseInterface.ipc_param_list = param_list
        BaseInterface.sync_ipc_data()

        msg_handler.initialize_pool(server)
        WebSocketServer.process_info("Receive Loop")
        sel = selectors.DefaultSelector()
        sel.register(server.__server_fileobj, selectors.EVENT_READ, WebSocketServer.accept)
        fd_conn_mgr = dict()

        last_heartbreak_time = time.clock()
        while True:
            # 检测所有的fileobj,是否有完成wait data的
            events = sel.select(timeout=1)
            for sel_obj, mask in events:
                # print(f"continue..{sel_obj}  {mask}")
                callback = sel_obj.data
                callback(server, sel_obj.fileobj, sel, mask, msg_handler, fd_conn_mgr)

            # 检测所有fd是否有效
            # for k, v in server.__heartcheck_mgr.items():
            #     server.__heartcheck_mgr[k] -= 1
            #     if v <= 0:
            #         conn = fd_conn_mgr[k]
            #         sel.unregister(conn)
            #         server.__remove_conn(conn)
            #         conn.close()
            #         del fd_conn_mgr[k]

            # 发送心跳检测包 间隔2秒
            # if time.clock() - last_heartbreak_time > 2:
            #     last_heartbreak_time = time.clock()
            #     for k, v in server.__conn_mgr.items():
            #         server.push_msg(k, msg_type=2, msg_body="")

    @staticmethod
    def write_main_loop(server):
        WebSocketServer.process_info("Send Loop")
        while True:
            try:
                exclusive_dict = server.__ipc_queue.get(timeout=1)
                fd = exclusive_dict.get("fd", None)
                msg = exclusive_dict.get("msg", None)
                if fd and msg:
                    handler = server.__get_protocol_handler(fd)
                    if handler:
                        handler.send_msg(msg)
            except Exception:
                pass
            finally:
                pass

    def __add_conn(self, conn):
        self.__conn_mgr[conn.fileno()] = conn
        self.__protocol_handler_mgr[conn.fileno()] = WebSocketProtocolHandler(conn)
        self.__heartcheck_mgr[conn.fileno()] = 5

        print(f"fd: {conn.fileno()} join {conn}")
        self.push_msg(fd=conn.fileno(), msg_type=1, msg_body=conn.fileno())
        if self.connect_callback is not None:
            for f in self.connect_callback:
                f(conn.fileno())

    def __remove_conn(self, conn):
        if conn.fileno() in self.__conn_mgr.keys():
            del self.__conn_mgr[conn.fileno()]
        if conn.fileno() in self.__protocol_handler_mgr.keys():
            del self.__protocol_handler_mgr[conn.fileno()]
        if conn.fileno() in self.__heartcheck_mgr.keys():
            del self.__heartcheck_mgr[conn.fileno()]

        print(f"fd: {conn.fileno()} leave {conn}")
        if self.disconnect_callback is not None:
            for f in self.disconnect_callback:
                f(conn.fileno())

    def __get_protocol_handler(self, fd):
        return self.__protocol_handler_mgr.get(fd, None)

    def reset_heartbreak(self, fd):
        self.__heartcheck_mgr[fd] = 5

整个代码分为3个部分,客户端连接管理、收数据、发数据。
首先是客户端连接管理,这是传统的网络编程
然后是收数据、发数据各一个进程处理,和主进程区分开来。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值