Найти - Пользователи
Полная версия: Asyncore и отключение по таймауту
Начало » Network » Asyncore и отключение по таймауту
1 2
NaganoEx
Ребята помогите победить.
Есть типовой сервер на asyncore. Всё работает замечательно, но засада в том, что никак не могу сделать, чтобы был какой-то таймер для таймаута.
Нужно, чтобы если от клиента не поступило данных в течение 30 секунд, его отключало и закрывало сокет.

import asyncore
import socket
class EchoHandler(asyncore.dispatcher_with_send):
    def handle_read(self):
        data = self.recv(8192)
        if data:
            print 'Received data = ', data
            self.send(data)
    def handle_error(self):
        print 'Error'
    def handle_close(self):
        print 'Client disconnected normal'
class EchoServer(asyncore.dispatcher):
    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)
    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            sock, addr = pair
            print 'Incoming connection from %s' % repr(addr)
            handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()
PooH
Насколько я понимаю, asyncore.dispatcher работает с сокетом в неблокирующем режиме, так что для сокета таймоут поставить не получится, придется делать самостоятельно. Я вот примерно набросал.
import asyncore
import socket
import time
from collections import defaultdict
class TimeoutController(object):
    TICK = 10.0
    TIMEOUT = 30.0
    def __init__(self):
        self.connections = defaultdict(float)
    def loop(self):
        while True:
            asyncore.loop(self.TICK, count=1)
            self.check_connections()
    def check_connections(self):
        now = time.time()
        for conn_id, (connection, last) in self.connections.items():
            if now - last > self.TIMEOUT:
                print 'Close client by timeout'
                del self.connections[conn_id]
                connection.close()
    def checkin(self, connection):
        self.connections[id(connection)] = (connection, time.time())
class EchoHandler(asyncore.dispatcher_with_send):
    def __init__(self, controller, sock=None, map=None):
        self.controller = controller
        controller.checkin(self)
        asyncore.dispatcher_with_send.__init__(self, sock, map)
    def handle_read(self):
        data = self.recv(8192)
        self.controller.checkin(self)
        if data:
            print 'Received data = ', data
            self.send(data)
    def handle_error(self):
        print 'Error'
    def handle_close(self):
        print 'Client disconnected normal'
        self.close()
        
        
class EchoServer(asyncore.dispatcher):
    def __init__(self, host, port, controller):
        asyncore.dispatcher.__init__(self)
        self.controller = controller
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)
    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            sock, addr = pair
            print 'Incoming connection from %s' % repr(addr)
            EchoHandler(self.controller, sock)
controller = TimeoutController()
server = EchoServer('localhost', 8080, controller)
controller.loop()

ЗЫ: А зачем вы вообще эту рухлядь схватили?
NaganoEx
PooH
ЗЫ: А зачем вы вообще эту рухлядь схватили?

А больше ничего и нет, либо голые сокеты либо asyncore. Я сначала думал можно сделать свой сервер на торнадо но там только предопределенные серверы, а мне нужен самый простой многопоточный сервер для обмена бинарными данными. Но очень важный момент, чтобы если по сокету не идут данные в течение n секунд, то он закрывался принудительно. Читал twisted но там класс для передачи линий(строк) для чата чтоли заточен… LineReader. Мне нужно просто байтики слать, и чтобы хотяб 100 подключений держал. Спасибо за пример, попробую вечером поработать.
sander
NaganoEx
но есть же asyncio
NaganoEx
sander
но есть же asyncio

Почитаю, если в ней можно выставить время таймаута, то будет то что надо!
NaganoEx
PooH
Я вот примерно набросал.

Спасибо большущее! Работает хорошо и как нужно, сейчас буду разбираться как вы такое соорудили!
NaganoEx
PooH а подскажите пожалуйста, почему при connection.close() не вызывается событие def handle_close(self): ?
NaganoEx
Какая-то засада, в случае если клиент отключается хорошо например через 5 сек. после подключения, то через 25 сек всё равно срабатывает таймер и снова закрывает сокет
PooH
NaganoEx
почему при connection.close() не вызывается событие def handle_close(self): ?
handle_close вызывается когда соединение закрывают на другой стороне, заметьте я на это событие вызываю self.close() чтобы закрыть его со своей, посмотрите про двухфазное закрытие соединения в протоколе TCP хотя бы тут

Если используете третий питон, то не раздумывая берите asyncio, это развитой и переработанный asyncore. Вот посмотрите.
PooH
NaganoEx
Какая-то засада, в случае если клиент отключается хорошо например через 5 сек. после подключения, то через 25 сек всё равно срабатывает таймер и снова закрывает сокет
Мой косяк. Набрасывал на коленке и не подумал об этом, надо у TimeoutController сделать примерно такой метод
def checkout(self, connection):
   del self.connections[id(connection)]
и вызывать его в handle_close.
Вообще принцип очень простой - храним словарь соединение - время последнего приема и по таймеру его просматриваем, если время превышено убиваем соединение. Я забыл просто удалять запись из словаря при нормальном закрытии
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB