2013-06-25 88 views
0

我有Python的TCP服務器asyncore:asyncore靠近舊插座

class AsyncClientHandler(asyncore.dispatcher_with_send): 
    def __init__(self,sock): 
     asyncore.dispatcher_with_send.__init__(self,sock) 

     self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 
     self.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 

     self.message="" 
     self.protocol=Protocol(DBSession, logger) 

    def handle_read(self): 
     data = self.recv(8192) 
     if data: 
      self.message+=data 
      while TERMINATOR in self.message: 
       index=self.message.index(TERMINATOR) 
       msg=self.message[:index] 
       self.message=self.message[index+len(TERMINATOR):] 

       answer=self.protocol.process_msg(msg, DBSession, tarif_dict) 
       if answer: 
        msg = HEADER+answer+TERMINATOR 
        self.send(msg) 

    def handle_close(self): 
     self.close() 

class AsyncServer(asyncore.dispatcher): 
    def __init__(self, host, port): 
     asyncore.dispatcher.__init__(self) 

     self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 

     self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 
     self.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 

     self.set_reuse_addr() 
     self.bind((host, port)) 
     self.listen(5) 

    def handle_accept(self): 
     pair = self.accept() 
     if pair is None: 
      pass 
     else: 
      sock, addr = pair 
      logging.info("Incoming connection from %s",repr(addr)) 
      AsyncClientHandler(sock) 

有些客戶端不關閉連接,所以在某些時候服務器崩潰,由於大量的插座。

我怎麼能在一段時間後關閉非活動插座? settimeout不起作用。

回答

0

要做到這一點,你可以使用TCP的保持連接(像你已經做了),並設置其延遲,坪......但這apporach應該只用於持久連接,並且僅適用於Unix系統。請閱讀here

還可以使插座的一些安排,在經過一段時間關閉它們或推遲他們時,他們是活躍的。我做出了表率與您的代碼的工作:

import sched, time 

class SocketSched(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     self.daemon = True 
     self.to_run = [] 
     self.scheds = {} 
     self.start() 

    def add(self, what): 
     self.to_run.append(what.values()[0]) 
     self.scheds.update(what) 

    def run(self): 
     while True: 
      if self.to_run: 
       run = self.to_run.pop() 
       if not run.empty(): run.run() 
       else: self.to_run.append(run) 

這裏我們定義了新的類在不同的線程調度的 - ,sched模塊將繼續阻止,像asyncore.loop()這是非常重要的。 這需要modificating你的代碼位:

class AsyncClientHandler(asyncore.dispatcher_with_send): 
    def __init__(self,sock, sch_class): 
     ... 
     self.delay = 10 
     self.sch_class = sch_class 
     self.sch = sched.scheduler(time.time, time.sleep) 
     self.sch_class.add({self.fileno(): self.sch}) 
     self.event = self.sch_class.scheds[self.fileno()].enter(self.delay, 1, self.handle_close,()) 

    ... 

    def delay_close(self): 
     self.sch_class.scheds[self.fileno()].cancel(self.event) 
     self.event = self.sch_class.scheds[self.fileno()].enter(self.delay, 1, self.handle_close,()) 

    ... 

    def handle_close(self): 
     try: 
      self.sch_class.scheds[self.fileno()].cancel(self.event) 
     except: 
      pass 
     ... 

self.delay是秒的超時。在這段時間過去之後,沒有任何行動延遲它,套接字將被關閉。 handle_close()中的行確保它不會因調度程序中的任務而被調用兩次。

現在你必須添加self.delay_close()到每一個方法的開頭,確保插座是活動的,如。 handle_read()

服務器類(例如獲取的SocketSched並將它傳遞給新的渠道):

class AsyncServer(asyncore.dispatcher): 
    def __init__(self, host, port, sch_class): 
     ... 
     self.sch_class = sch_class 

    ... 

    def handle_accept(self): 
    ... 
      AsyncClientHandler(sock, self.sch_class) 

就緒。使用此方法:

server = AsyncServer('', 1337, SocketSched()) 
asyncore.loop() 

此解決方案可以正常工作,但在某些關閉事件中可能會出現錯誤。無論如何,當發生超時時,套接字將讀取,延遲並關閉。不幸的是運行這樣的調度循環使用一些CPU