Уведомления

Группа в Telegram: @pythonsu

#1 Апрель 14, 2015 15:50:44

NaganoEx
Зарегистрирован: 2015-04-14
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

Ребята помогите победить.
Есть типовой сервер на 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()

Офлайн

#2 Апрель 16, 2015 09:03:13

PooH
От:
Зарегистрирован: 2006-12-05
Сообщения: 1948
Репутация: +  72  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

Насколько я понимаю, 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()

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



Вот здесь один из первых отарков съел лаборанта. Это был такой умный отарк, что понимал даже теорию относительности. Он разговаривал с лаборантом, а потом бросился на него и загрыз…

Отредактировано PooH (Апрель 16, 2015 09:06:38)

Офлайн

#3 Апрель 16, 2015 12:58:59

NaganoEx
Зарегистрирован: 2015-04-14
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

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

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

Офлайн

#4 Апрель 16, 2015 13:38:15

sander
Зарегистрирован: 2015-02-19
Сообщения: 317
Репутация: +  53  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

NaganoEx
но есть же asyncio

Офлайн

#5 Апрель 16, 2015 14:38:39

NaganoEx
Зарегистрирован: 2015-04-14
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

sander
но есть же asyncio

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

Офлайн

#6 Апрель 16, 2015 15:18:37

NaganoEx
Зарегистрирован: 2015-04-14
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

PooH
Я вот примерно набросал.

Спасибо большущее! Работает хорошо и как нужно, сейчас буду разбираться как вы такое соорудили!

Офлайн

#7 Апрель 16, 2015 15:25:39

NaganoEx
Зарегистрирован: 2015-04-14
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

PooH а подскажите пожалуйста, почему при connection.close() не вызывается событие def handle_close(self): ?

Офлайн

#8 Апрель 16, 2015 18:03:03

NaganoEx
Зарегистрирован: 2015-04-14
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

Какая-то засада, в случае если клиент отключается хорошо например через 5 сек. после подключения, то через 25 сек всё равно срабатывает таймер и снова закрывает сокет

Офлайн

#9 Апрель 16, 2015 18:10:08

PooH
От:
Зарегистрирован: 2006-12-05
Сообщения: 1948
Репутация: +  72  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

NaganoEx
почему при connection.close() не вызывается событие def handle_close(self): ?
handle_close вызывается когда соединение закрывают на другой стороне, заметьте я на это событие вызываю self.close() чтобы закрыть его со своей, посмотрите про двухфазное закрытие соединения в протоколе TCP хотя бы тут

Если используете третий питон, то не раздумывая берите asyncio, это развитой и переработанный asyncore. Вот посмотрите.



Вот здесь один из первых отарков съел лаборанта. Это был такой умный отарк, что понимал даже теорию относительности. Он разговаривал с лаборантом, а потом бросился на него и загрыз…

Офлайн

#10 Апрель 16, 2015 18:17:13

PooH
От:
Зарегистрирован: 2006-12-05
Сообщения: 1948
Репутация: +  72  -
Профиль   Отправить e-mail  

Asyncore и отключение по таймауту

NaganoEx
Какая-то засада, в случае если клиент отключается хорошо например через 5 сек. после подключения, то через 25 сек всё равно срабатывает таймер и снова закрывает сокет
Мой косяк. Набрасывал на коленке и не подумал об этом, надо у TimeoutController сделать примерно такой метод
def checkout(self, connection):
   del self.connections[id(connection)]
и вызывать его в handle_close.
Вообще принцип очень простой - храним словарь соединение - время последнего приема и по таймеру его просматриваем, если время превышено убиваем соединение. Я забыл просто удалять запись из словаря при нормальном закрытии



Вот здесь один из первых отарков съел лаборанта. Это был такой умный отарк, что понимал даже теорию относительности. Он разговаривал с лаборантом, а потом бросился на него и загрыз…

Отредактировано PooH (Апрель 16, 2015 18:20:05)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version