1

asyncoreを使用してpythonでtcpサーバーを取得しました:

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 が機能しません。

4

1 に答える 1

0

これを実現するには、TCP のキープアライブを使用して (既に行ったように)、その遅延、ping を設定できます...ただし、このアプローチは長時間の接続にのみ使用する必要があり、Unix でのみ使用できます。ここを読んでください。

また、ソケットのスケジュールを作成して、時間が経過したらソケットを閉じたり、アクティブになったときにソケットを遅らせたりすることもできます。私はあなたのコードで動作する例を作りました:

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()ます。これには、コードを少し変更する必要があります。

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() の行は、スケジューラのタスクが原因で 2 回呼び出されないようにします。

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 が使用されます。

于 2013-08-03T15:47:52.853 に答える