示例#1
0
文件: udp.py 项目: skripkar/noc
class UDPSocket(object):
    """
    UDP socket abstraction

    @tornado.gen.coroutine
    def test():
        sock = UDPSocket()
        # Send request
        yield sock.sendto(data, (address, port))
        # Wait reply
        data, addr = yield sock.recvfrom(4096)
        # Close socket
        sock.close()
    """
    def __init__(self, ioloop=None, tos=None):
        self.ioloop = ioloop or IOLoop.current()
        self.send_buffer = None  # (data, address)
        self.bufsize = None
        self.timeout_task = None
        self.socket = None
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        if tos:
            self.socket.setsockopt(
                socket.IPPROTO_IP, socket.IP_TOS, tos
            )
        self.fd = self.socket.fileno()
        self.socket.setblocking(0)
        self.future = None
        self.timeout = None
        self.events = None

    def __del__(self):
        self.close()

    def get_future(self):
        """
        Get future and start timeout task when needed
        """
        if not self.future or self.future.done():
            self.future = TracebackFuture()
            self.start_timeout()
        return self.future

    def settimeout(self, timeout):
        """
        Set timeout for following blocking operations
        """
        self.stop_timeout()
        self.timeout = timeout

    def get_timeout(self):
        return self.timeout

    def start_timeout(self):
        self.stop_timeout()
        if self.timeout:
            self.timeout_task = self.ioloop.call_later(
                self.timeout,
                self.on_timeout
            )

    def stop_timeout(self):
        if self.timeout_task:
            self.ioloop.remove_timeout(self.timeout_task)
            self.timeout_task = None

    def add_handler(self, callback, events):
        self.remove_handler()
        self.ioloop.add_handler(self.fd, callback, events)
        self.events = events

    def remove_handler(self):
        if self.events:
            self.ioloop.remove_handler(self.fd)
            self.events = 0

    def recvfrom(self, bufsize):
        future = self.get_future()
        try:
            data, addr = self.socket.recvfrom(bufsize)
            self.remove_handler()
            future.set_result((data, addr))
        except socket.error as e:
            c = errno_from_exception(e)
            if c in _ERRNO_WOULDBLOCK:
                self.bufsize = bufsize
                self.add_handler(self.on_read, IOLoop.READ)
            else:
                future.set_exception()
        return future

    def sendto(self, data, address):
        future = self.get_future()
        try:
            r = self.socket.sendto(data, address)
            self.remove_handler()
            future.set_result(r)
        except socket.error as e:
            c = errno_from_exception(e)
            if c in _ERRNO_WOULDBLOCK:
                # Wait for socket is ready to write
                self.send_buffer = (data, address)
                self.add_handler(self.on_write, IOLoop.WRITE)
            else:
                future.set_exception(e)
        return future

    def on_read(self, fd, events):
        self.recvfrom(self.bufsize)

    def on_write(self, fd, events):
        self.ioloop.remove_handler(self.fd)
        data, address = self.send_buffer
        self.send_buffer = None
        self.sendto(data, address)

    def on_timeout(self):
        if self.future and self.future.running():
            self.timeout_task = None
            try:
                raise socket.timeout()
            except Exception as e:
                self.future.set_exception(e)

    def close(self):
        if self.timeout_task:
            self.ioloop.remove_timeout(self.timeout_task)
            self.timeout_task = None
        if self.socket:
            self.remove_handler()
            self.socket.close()
            self.socket = None