游戏服务器之长连接服务器(python)(2)

游戏服务器之长连接服务器实现tcp连接的数据异步收发。

一个网络收发处理进程,一个服务器对象逻辑处理进程。两个进程之间使用管道通信。

网络收发处理进程:

(1)网络处理是由反应器的子线程来处理的。

(2)从管道的一端读取数据,读取管道后和网络发送前,需要前处理连包和反序列化来检查包的完整性。这里还缺少合适的连包断包处理。

(3)反应器的子线程接受网络数据再写到管道。

服务器对象逻辑处理进程:

(1)读取网络数据并反序列化的是在服务器对象进程的一个接受循环线程里处理。并派送到服务器对象的消息队列。

(2)反应器的处理定时器事件(在主线程)。

(3)主线程序列化并发送到管道。

这样的好处是逻辑服务进程只处理逻辑相关的事情,网络收发处理进程只是处理网络数据收发(可以使用两个进程的两个反应器分开处理,一个进程只可以使用一个反应器),可以异步处理

缺点是网络收发处理进程和服务器对象逻辑处理进程都需要连包和反序列化来检查数据的完整性,使用管道也会有消耗。

总的来说,使用管道发送局限于单一物理机器,序列化反序列化次数增加,这种方法没有提升效率。需要在网络收发进程增加连包段宝处理需要拓展到适用于服务器集群。


1、网络收发进程服务器对象

(1)网络收发进程服务器对象定义

class BaseNetServer:
    u"""
    . 子进程,模拟网络模块
    . 发送消息过程:从线程从管道中接收消息,
    .                查看消息目标服务
    .                根据目标服务查找通信频道
    .                调用通信频道发送消息
    . 接收消息:通信频道将消息回调给网络服务
    .            网络服务将消息通过管道发送给主服务
    """
    def __init__(self, recv_pipe, send_pipe, conf):
         log.info(u"netserver init")
        self.recv_pipe = recv_pipe
        self.send_pipe = send_pipe
        
        self.btime = time.time()
        self.etime = 0
        self.count = 0
        
        self.conf = conf
        self.name = conf.myself.name
        self.channels = {} # server_name: channel
        self.route = {} # service_id: ServiceItem
        for s in conf.servers:
            for svcId, svc in s.services.items():
                self.route[svcId] = ServiceItem(svcId, s.name)
                
        # 创建工作线程
        self.thread = Thread(target=self.run)
        self.thread.setDaemon(True)
        self.thread.start()
        
        # 主线程loop,启动网络模块
        print "------------------------------"
        self.start()
    # -------------------------------------------------------------------------
    def requestCreateChannel(self, target, transport):
        u"""
        . 网络层在网络协议建立后,请求服务器对象建立和对端Server的通讯频道
        1)服务器对象创建Channel对象
        2)服务器对象为每个服务设置通讯Channel"""
        channel = Channel(transport)
        self.channels[target] = channel
        for k, v in self.route.items():
            if v.serverName == target:
                v.channel = channel
                log.info(u"service (%d) is in the server(%s)", k, target)
        log.info(u"[%s] : server(%s) is connected", self.name, target)
        channel.setListener(self)
        return channel

    def requestDestroyChannel(self, target):
        u"""
        .网络层在网络协议断连后,请求服务器对象销毁和对端Server的通讯频道
        1)服务器对象销毁Channel对象
        2)服务器对象设置相应服务Channel=None,后续发给该服务的消息将无法送达
        """
        del self.channels[target]
        for k, v in self.route.items():
            if v.serverName == target:
                v.channel = None
        log.info(u"server(%s) is disconnected", target)
    
    def getChannel(self, serviceId):
        item = self.route[serviceId]
        if item != None:
            return item.channel
        return None
        
    # -------------------------------------------------------------------------
    def start(self):#建立需求的工厂
        conf = self.conf
        log.info(u"Setup network configuration....")
        for i in range(0, len(conf.servers)):
            if conf.servers[i].name == self.name:
                reactor.listenTCP(conf.servers[i].port, 
                                  ServerConnectionFactory(self, self.name))
                break
            else:
                log.info(u"conf.servers[i].ip: %s, port: %s", \
                  conf.servers[i].ip, conf.servers[i].port)
                reactor.connectTCP(self.conf.servers[i].ip, conf.servers[i].port,
                  ClientConnectionFactory(self, self.name))
                 
        log.info("network recv task run")
        try :
            reactor.run()
        except:
            reactor.stop()
    
    def run(self):#读取管道数据并发送  
        log.info("network send task run")
        while True:
            try:
                data = self.send_pipe.recv()#接受到的逻辑进程发来的网络数据则转发出去
                self.send(data)
            except:
                traceback.print_exc()
    
    def send(self, data):#管道发送到逻辑处理进程
        u"""
        . 发送消息
        """
        self.count += 1
        if self.count%10000==0:
            log.info("network send event:%d", self.count)
            
        (rc, event) = PipeEvent.createEventFromStream(data)
        if rc:
            channel = self.getChannel(event.dstId)
            if channel!=None:
                channel.send(event.eventData)
            else:
                log.error("service:%d channel not find", event.dstId)
        else:
            log.error("create pipe event error")

    def on_event(self, event):
        u"""
        . event: PipeEvent
        """
        try:
            self.recv_pipe.send(event.eventData)
        except:
            traceback.print_exc()

(2)启动网络收发进程服务器对象

读取配置文件启动子服务对象。

def start_net_server(recv_queue, send_queue, file):
    u"""创建子进程服务对象"""
    log.info(u"System Starting -> loading configuration %s" , file)
    conf = SystemConfig(file)
    net_server = BaseNetServer(recv_queue, send_queue, conf)


2、逻辑处理服务器对象 

逻辑处理服务器对象接受网络收发进程发送来的数据,反序列化后检查事件的合法性和完整性,然后派送到具体的逻辑服务并进行处理。

class TaskServer:
    u"""
    .服务器对象,各个服务都通过服务器对象,可进行
    1)服务之间通讯
    2)读取配置

    .服务器对象主要的功能包括
    1) 初始化
    2)读取配置
    3)装载服务
    4)实现通讯机制

    .服务器维护以下关键数据结构
    1)路由表:当收到事件后需要查询路由表寻找事件的接收服务器
    2)服务表:该表保存服务数据,服务和网络的关系
    3)通讯频道表:该表保存当前的通道数据信息
    """
    def __init__(self, conf, file):#file配置文件,conf配置文件的解析对象
        self.conf = conf
        self.route = {} # service_id: ServiceItem
        self.services = {} # service_id: service object
        self.lock = Lock()
        self.name = conf.myself.name
        
        self.recv_pipe, self.child_recv_pipe = Pipe()#接收管道的两端
        self.send_pipe, self.child_send_pipe = Pipe()#发送管道的两端
        self.network = Process(target=start_net_server, args=(self.child_recv_pipe, \
                                                        self.child_send_pipe, file))#管道网络进程处理收发,进程是创建子进程服务器对象
        self.network.start()#启动网络进程
        self.init_server_data()
        # create route table
        for s in conf.servers:
            for svcId, svc in s.services.items():
                self.route[svcId] = ServiceItem(svcId, s.name)
        
        global SERVER
        SERVER = self
        
    def init_server_data(self):
        log.info("begin init data")
        ServerData.init_load()
        log.info("end init data")
                
    def getServerConfig(self, serverName):
        u"""获得指定服务器的配置信息"""
        for server in self.conf.servers:
            if server.name == serverName:
                return server
        return None
    def getServiceConfig(self, serviceId):
        u"""获得指定服务的配置信息"""
        return self.conf.myself.services[serviceId]
                
    # -----  service ----------------------------------------------------------
    def registeService(self, service):
        u"""服务注册函数,服务注册成功后,则可以收发消息"""
        self.services[service.serviceId] = service
    
    def unregisteService(self, service):
        u"""服务注销函数,服务注册成功后,则可以收发消息"""
        del self.services[service.serviceId]
    
    # ----- event ------------------------------------------------------------
    
    def onChannelEvent(self, event):
        u"""
        Channel收到相应的事件后将调用服务器对象的onChannelEvent
       . 服务器对象根据dstId,转发给相应的本地服务
        """
        self._dispatchEvent(event)
        
    def _sendEvent(self, buf):#通过管道进程来发送
        self.send_pipe.send(buf)
               
    def sendToPipe(self, event):#管道发送
        u"""event:"""
        self.send_pipe.send(event.toStream())#reactor.callFromThread(self._sendEvent, event.toStream())

    def sendEvent(self, eventData, srcId= -1, dstId= -1, eventType= -1, \
                            param1= -1, param2= -1, senceId=-1, origEvent=None):
        u"""
        .可通过调用本函数,发送事件给指定服务
        1)服务器对象首先检查目标服务的位置
        2)对于远程服务,通过Channel对象发送事件至对端服务器
        3)对于本地服务,则直接转发
        eventData : 发送的数据
        srcId : 源服务的标记,如origEvent不为空,则为origEvent.dstId
        dstId : 目标服务的标记,如origEvent不为空,则为origEvent.srcId
        eventType: 事件  型,如果不填写则为-1
        origEvent: 为源事件
        """
        # check whether event should be sent over network or not
        if origEvent != None:
            srcId = origEvent.dstId
            dstId = origEvent.srcId
            param1 = origEvent.param1
            param2 = origEvent.param2
            senceId = origEvent.senceId
            
        item = self.route[dstId]

        if item == None:
            log.warning(u"Event(%s) Missing due to no this service(%d)", \
              eventData, dstId)
            return - 1

        event = Event.createEvent(srcId, dstId, eventType, param1, param2, eventData, senceId)     
        if item.serverName == self.name: # local event
            self._dispatchEvent(event)
        else:
            self.sendToPipe(event)#不是本服务器进程的发送到别的服务器进程
        return event.tranId
    
    def notifyEvent(self, userId, data, event_type, sceneId=0, serviceId=0):
        u"""
        . 发送通知事件给客户端
        """
        self.sendEvent(data, serviceId, NOTIFY_SERVICE, \
                     NOTIFY_SERVICE_SENDTO_CLIENT.REQ, userId, event_type, sceneId)

    def _dispatchEvent(self, event):
        u"""分发事件至本地服务的函数"""
        svc = self.services[event.dstId]
        if svc != None:
            try :
                svc.dispatch(event)
            except Full, e:
                log.warning(u"Event(%s) Missing due to queue is full", event) 
        else:
            log.warning(u"Event(%s) Missing due to no this service", event)

    def _dispatchTimerEvent(self, event):
        u"""分发时间事件至本地服务的函数"""
        svc = self.services[event.dstId]
        if svc != None:
            svc.dispatchTimerEvent(event)
        else:
            log.warning(u"Event(%s) Missing due to no this service", event)
            
    def localBroadcastEvent(self, eventType, param1, param2, eventData):
        u"""实现本地服务广播"""
        for id, svc in self.services.items():
            evt = Event.createEvent(-1, svc.serviceId, eventType, param1, \
              param2, eventData)
            self._dispatchEvent(evt)
    # ----- timer -------------------------------------------------------------


    def setTimerByFunc(self, delay, func, *args, **kw):
        u"""设置定时器,并通过另外的函数唤醒"""
        reactor.callLater(delay, func, args, kw)
    
    def setTimerByClientEvent(self, delay, sid, event):
        u"""
        .设置定时器,直接分发调用者传入的事件,也是通过消息队列的方式返回给客户端
        """
        def onTime(server, sid, evt):
            svc = self.services[sid]
            if svc != None:
                svc.dispatchTimerEvent(evt)
            else:
                log.warning(u"Event(%s) Missing due to no this service", event)
            
        return reactor.callLater(delay, onTime, self, sid, event)


    def setTimerByEvent(self, delay, sid, eventData):
        u"""设置定时器的函数"""
        event = Event.createEvent(0, sid, 1, -1, -1, eventData)
        def onTime(server, evt):
            self._dispatchTimerEvent(evt)

        return reactor.callLater(delay, onTime, self, event)
    
    # ----- run --------------------------------------------------------------
        
    def installServices(self):
        u"""通过配置文件的信息,启动,初始化和注册服务"""
        for svcId, svc in self.conf.myself.services.items():
            name = svc.options["module"]
            __import__(name)
            module = sys.modules[name]
            log.info(u"install service : %s", name)
            print "----- install service:", name
            
            self.registeService(eval(svc.options["code"])) # TODO: ?
    
    def run(self, file):
        u"""
        .主循环函数
        1)创建 网络线程
        2) 监听接收管道
        """
        self.thread = Thread(target=self.recv_loop)#
        self.thread.setDaemon(True)
        self.thread.start()
        log.info("reactor run")
        try :
            reactor.run()
        except:
            reactor.stop()

    def recv_loop(self):
        log.info("recv loop")
        while True:
            try:
                data = self.recv_pipe.recv()#监听管道的事件
                (rc,event) = Event.createEventFromStream(data)
                if rc:
                    self.onChannelEvent(event)#把消息派送到服务的消息队列
                else:
                    log.error("create event error")
            except:
                traceback.print_exc()
            
    def stop(self):
        u"""停止各个服务"""
        for service in self.services.values():
            service.stop()
            
        #time.sleep(5)
        for service in self.services.values():
            self.unregisteService(service)
        log.debug("stop server")


3、会话

class Channel:
	u"""
	.Channel类用于和网络上其它服务器进行通讯,对于每个连接的Server,本服务器会维护一个Channel实例
	.用于接收和发送相关事件消息
	.Channel会注册至服务器上,从网络上收到消息后会缓冲以及解析,生成事件对象并交服务器处理
	.Channel会打包发送的数据,并通过Twisted提供的传输机制来发送数据,Channel对象不进行数据缓存,
	.Twisted会解决发送过程中的缓存问题
	"""
	def __init__(self, transport):
	u"""
	.通过建立的网络传输对象,初始化Channel实例
	"""
	self.transport = transport
	self.buffer = ""
	self.listener = None

	def send(self, buf):
		u"""
		. 发送消息方法
		. 该方法会使用反应器的callFromThread方法进行处理
		. 1) 解决线程安全的问题
		. 2) 解决网络发送的问题
		. 注意:trasport.write方法不是线程安全的方法
		"""
		#self._sendEvent(buf)
		reactor.callFromThread(self._sendEvent, buf) # NOTE: reactor保证线程安全

	def _sendEvent(self, buf):
		self.transport.write(buf)

	def writeBuffer(self, data):#接收数据,检查完整性,写到管道
	    u"""
	    . 在网络上收到消息后,该方法将被调用
	    . 该方法缓存并解析事件,同时交监听器处理
	    """
	    self.buffer += data
	    while True: 
		(result, event) = PipeEvent.createEventFromStream(self.buffer)#检查事件合法性(这里暂时不能优化,需要在管道保证消息数据接收完整性)
		if result:
			self.buffer = self.buffer[event.length:]
		if (self.listener != None):
		    self.listener.on_event(event)
		else:
		    return

	def setListener(self, listener):
	    u""" listener: server.onChannelEvent """
	    self.listener = listener

4、管道事件

class PipeEvent:
	u"""
		. 进程管道传输事件
		"""
	def __init__(self, ver, length, sid, did, eventType, eventData):
		self.version = ver
		self.length = length
		self.srcId = sid
		self.dstId = did
		self.eventType = eventType
		self.eventData = eventData
			
	@staticmethod
	def createEventFromStream(data):
		u"""
		.return: (boolean, Event | None)
		"""
		l = len(data)
		if l < lengthOfHeader:
			return (False, None)
		(ver, length, sid, did, eventType) = struct.unpack_from(formatOfHeader, data)
		if l < length:
			return (False, None)
		return (True, PipeEvent(ver, length, sid, did, eventType, data[:length]))

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要连接Python服务器,您可以按照以下步骤进行操作: 1. 在服务器上创建新的Python环境。您可以使用MobaXterm或其他适合您的工具,在服务器上创建一个新的conda环境。例如,使用以下命令在服务器上创建一个名为tf1.10的新环境,并指定Python版本为3.6: ``` conda create -n tf1.10 python=3.6 ``` 2. 激活新的环境。使用以下命令激活刚刚创建的tf1.10环境: ``` source activate tf1.10 ``` 3. 下载安装TensorFlow。使用pip命令下载并安装特定版本的TensorFlow。例如,使用以下命令安装TensorFlow 1.10版本: ``` pip install tensorflow-gpu==1.10 ``` 4. 下载安装Keras。使用pip命令下载并安装特定版本的Keras。例如,使用以下命令安装Keras 2.2版本: ``` pip install keras==2.2 ``` 5. 在PyCharm中设置远程连接配置。打开PyCharm,并选择 "File" -> "Settings" -> "Python Interpreter"。在弹出的窗口中,填写服务器的IP地址和用户名,并点击 "Next"。输入密码并点击 "Next"。完成设置后,即可连接到Python服务器。 请注意,上述步骤中的命令和操作可能因工具版本和配置而有所不同。请根据您实际使用的工具和服务器配置进行相应的操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [python连接服务器完整过程](https://blog.csdn.net/loralq/article/details/118762814)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值