def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start()
def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start()
def __init__(self, server_address, multicast=False, starting_mid=None): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() host, port = server_address ret = socket.getaddrinfo(host, port) family, socktype, proto, canonname, sockaddr = ret[0] self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast # IPv4 or IPv6 if len(sockaddr) == 4: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.multicast: # Set some options to make it multicast-friendly try: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except AttributeError: pass # Some systems don't support SO_REUSEPORT self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind to the port self._socket.bind(self.server_address) # Set some more multicast options interface = socket.gethostbyname(socket.gethostname()) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface)) self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address) + socket.inet_aton(interface)) else: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation """ self._currentMID = starting_mid self._server = server self._callback = callback self._cb_ignore_read_exception = cb_ignore_read_exception self._cb_ignore_write_exception = cb_ignore_write_exception self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._receiver_thread = None
def __init__(self, server, starting_mid, callback, sock=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly """ self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._receiver_thread = threading.Thread( target=self.receive_datagram, name=threading.current_thread().name + '-Receive_Datagram')
def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) try: # legal socket.inet_aton(server[0]) except socket.error: # Not legal data = socket.getaddrinfo(server[0], server[1]) self._server = (data[0], data[1]) self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start()
class CoAP(object): """ Client class to perform requests to remote servers. """ def __init__(self, server, starting_mid, callback, sock=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly """ self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._receiver_thread = threading.Thread( target=self.receive_datagram, name=threading.current_thread().name + '-Receive_Datagram') def close(self): """ Stop the client. """ self._receiver_thread.join() self._socket.close() @property def current_mid(self): """ Return the current MID. :return: the current mid """ return self._currentMID @current_mid.setter def current_mid(self, c): """ Set the current MID. :param c: the mid to set """ assert isinstance(c, int) self._currentMID = c def send_message(self, message): """ Prepare a message to send on the UDP socket. Eventually set retransmissions. :param message: the message to send """ if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): """ Send a message over the UDP socket. :param message: the message to send """ host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) if not self._receiver_thread.isAlive(): self._receiver_thread.start() def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform( defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread( target=self._retransmit, name=threading.current_thread().name + '-Retransmit', args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet( ): logger.debug("retransmit Request") retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format( message=message.line_print)) message.timeouted = True try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def receive_datagram(self): """ Receive datagram from the UDP socket and invoke the callback function. """ logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: logger.debug("orderly shutdown on server end") return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response( message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request( transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty( transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: print 'orderly shutdown on server end' return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response(message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request(transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover # Handle separate """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): """ Client class to perform requests to remote servers. """ def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation """ self._currentMID = starting_mid self._server = server self._callback = callback self._cb_ignore_read_exception = cb_ignore_read_exception self._cb_ignore_write_exception = cb_ignore_write_exception self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._receiver_thread = None def close(self): """ Stop the client. """ self.stopped.set() for event in self.to_be_stopped: event.set() if self._receiver_thread is not None: self._receiver_thread.join() try: # Python does not close the OS FD on socket.close() # Ensure OS socket is closed with shutdown to prevent FD leak self._socket.shutdown(socket.SHUT_RDWR) except socket.error: pass self._socket.close() @property def current_mid(self): """ Return the current MID. :return: the current mid """ return self._currentMID @current_mid.setter def current_mid(self, c): """ Set the current MID. :param c: the mid to set """ assert isinstance(c, int) self._currentMID = c def send_message(self, message): """ Prepare a message to send on the UDP socket. Eventually set retransmissions. :param message: the message to send """ if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def end_observation(self, token): """ Remove an observation token from our records. :param token: the token for the observation """ dummy = Message() dummy.token = token dummy.destination = self._server self._observeLayer.remove_subscriber(dummy) @staticmethod def _wait_for_retransmit_thread(transaction): """ Only one retransmit thread at a time, wait for other to finish """ if hasattr(transaction, 'retransmit_thread'): while transaction.retransmit_thread is not None: logger.debug("Waiting for retransmit thread to finish ...") time.sleep(0.01) continue def _send_block_request(self, transaction): """ A former request resulted in a block wise transfer. With this method, the block wise transfer will be continued, including triggering of the retry mechanism. :param transaction: The former transaction including the request which should be continued. """ transaction = self._messageLayer.send_request(transaction.request) # ... but don't forget to reset the acknowledge flag transaction.request.acknowledged = False self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) def send_datagram(self, message): """ Send a message over the UDP socket. :param message: the message to send """ host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() raw_message = serializer.serialize(message) try: self._socket.sendto(raw_message, (host, port)) except Exception as e: if self._cb_ignore_write_exception is not None and callable(self._cb_ignore_write_exception): if not self._cb_ignore_write_exception(e, self): raise if self._receiver_thread is None or not self._receiver_thread.isAlive(): self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start() def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread = threading.Thread(target=self._retransmit, name=str('%s-Retry-%d' % (threading.current_thread().name, message.mid)), args=(transaction, message, future_time, 0)) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: logger.debug("retransmit loop ... enter") while retransmit_count <= defines.MAX_RETRANSMIT \ and (not message.acknowledged and not message.rejected) \ and not transaction.retransmit_stop.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not transaction.retransmit_stop.isSet(): retransmit_count += 1 future_time *= 2 if retransmit_count < defines.MAX_RETRANSMIT: logger.debug("retransmit loop ... retransmit Request") self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True # Inform the user, that nothing was received self._callback(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None logger.debug("retransmit loop ... exit") def receive_datagram(self): """ Receive datagram from the UDP socket and invoke the callback function. """ logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(0.1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except Exception as e: # pragma: no cover if self._cb_ignore_read_exception is not None and callable(self._cb_ignore_read_exception): if self._cb_ignore_read_exception(e, self): continue return else: # pragma: no cover if len(datagram) == 0: logger.debug("Exiting receiver Thread due to orderly shutdown on server end") return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): logger.debug("receive_datagram - " + str(message)) transaction, send_ack = self._messageLayer.receive_response(message) if transaction is None: # pragma: no cover continue self._wait_for_retransmit_thread(transaction) if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: self._send_block_request(transaction) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) logger.debug("Exiting receiver Thread due to request") def _send_ack(self, transaction): """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self._cb_ignore_listen_exception = cb_ignore_listen_exception addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock elif self.multicast: # pragma: no cover # Create a socket # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1])) addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0] group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
def __init__(self, server_address, multicast=False, starting_mid=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() host, port = server_address ret = socket.getaddrinfo(host, port) family, socktype, proto, canonname, sockaddr = ret[0] self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource("root", self, visible=False, observable=False, allow_children=False) root.path = "/" self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast # IPv4 or IPv6 if len(sockaddr) == 4: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.multicast: self._socket.bind(("", self.server_address[1])) try: group = socket.inet_aton(self.server_address[0]) except: group = self.server_address[0] mreq = struct.pack("4sL", group, socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) # # Set some options to make it multicast-friendly # try: # self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) # except AttributeError: # pass # Some systems don't support SO_REUSEPORT # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # # # Bind to the port # self._socket.bind(self.server_address) # # # Set some more multicast options # interface = socket.gethostbyname(socket.gethostname()) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface)) # self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address[0]) # + socket.inet_aton(interface)) else: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) try: # legal socket.inet_aton(server[0]) except socket.error: # Not legal data = socket.getaddrinfo(server[0], server[1]) self._server = (data[0], data[1]) self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._blockLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout, e: err = e.args[0] # this next if/else is a bit redundant, but illustrates how the # timeout exception is setup if err == 'timed out': continue else: print e return except socket.error, e: # Something else happened, handle error, exit, etc. print e return else:
class CoAP(object): def __init__(self, server_address, multicast=False, starting_mid=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0]) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) print "Yes Socket IPV6 is " print self.server_address self._socket.bind(self.server_address) def purge(self): """ Clean old transactions """ while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) if len(client_address) > 2: client_address = (client_address[0], client_address[1]) except socket.timeout: continue try: serializer = Serializer() message = serializer.deserialize(data, client_address) if isinstance(message, int): logger.error("receive_datagram - BAD REQUEST") rst = Message() rst.destination = client_address rst.type = defines.Types["RST"] rst.code = message rst.mid = self._messageLayer._current_mid self._messageLayer._current_mid += 1 % 65535 self.send_datagram(rst) continue logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated, transaction completed") if transaction.response is not None: self.send_datagram(transaction.response) return elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated, transaction NOT completed") self._send_ack(transaction) return args = (transaction, ) t = threading.Thread(target=self.receive_request, args=args) t.start() # self.receive_datagram(data, client_address) elif isinstance(message, Response): logger.error("Received response from %s", message.source) else: # is Message transaction = self._messageLayer.receive_empty(message) if transaction is not None: with transaction: self._blockLayer.receive_empty(message, transaction) self._observeLayer.receive_empty(message, transaction) except RuntimeError: print "Exception with Executor" self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_request(self, transaction): """ Receive datagram from the udp socket. :param data: the udp message :param client_address: the ip and port of the client """ with transaction: transaction.separate_timer = self._start_separate_timer(transaction) self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return self._observeLayer.receive_request(transaction) self._requestLayer.receive_request(transaction) if transaction.resource is not None and transaction.resource.changed: self.notify(transaction.resource) transaction.resource.changed = False elif transaction.resource is not None and transaction.resource.deleted: self.notify(transaction.resource) transaction.resource.deleted = False self._observeLayer.send_response(transaction) self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response) def send_datagram(self, message): """ :type message: Message :param message: """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :param path: the path for the new created resource :type resource: Resource :param resource: the resource to be added """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread(target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet(): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ Start a thread to handle separate mode. :type transaction: Transaction :param transaction: the transaction that is in processing :rtype : the Timer object """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,)) t.start() return t @staticmethod def _stop_separate_timer(timer): """ Stop the separate Thread if an answer has been already provided to the client. :param timer: The Timer object """ timer.cancel() def _send_ack(self, transaction): """ Sends an ACK message for the request. :param transaction: the transaction that owns the request """ ack = Message() ack.type = defines.Types['ACK'] # TODO handle mutex on transaction if not transaction.request.acknowledged and transaction.request.type == defines.Types["CON"]: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): """ Notifies the observers of a certain resource. :param resource: the resource """ observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: with transaction: transaction.response = None transaction = self._requestLayer.receive_request(transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response)
class CoAP(object): """ Client class to perform requests to remote servers. """ def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation """ self._currentMID = starting_mid self._server = server self._callback = callback self._cb_ignore_read_exception = cb_ignore_read_exception self._cb_ignore_write_exception = cb_ignore_write_exception self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._receiver_thread = None def close(self): """ Stop the client. """ self.stopped.set() for event in self.to_be_stopped: event.set() if self._receiver_thread is not None: self._receiver_thread.join() self._socket.close() @property def current_mid(self): """ Return the current MID. :return: the current mid """ return self._currentMID @current_mid.setter def current_mid(self, c): """ Set the current MID. :param c: the mid to set """ assert isinstance(c, int) self._currentMID = c def send_message(self, message): """ Prepare a message to send on the UDP socket. Eventually set retransmissions. :param message: the message to send """ if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) @staticmethod def _wait_for_retransmit_thread(transaction): """ Only one retransmit thread at a time, wait for other to finish """ if hasattr(transaction, 'retransmit_thread'): while transaction.retransmit_thread is not None: logger.debug("Waiting for retransmit thread to finish ...") time.sleep(0.01) continue def _send_block_request(self, transaction): """ A former request resulted in a block wise transfer. With this method, the block wise transfer will be continued, including triggering of the retry mechanism. :param transaction: The former transaction including the request which should be continued. """ transaction = self._messageLayer.send_request(transaction.request) # ... but don't forget to reset the acknowledge flag transaction.request.acknowledged = False self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) def send_datagram(self, message): """ Send a message over the UDP socket. :param message: the message to send """ host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() raw_message = serializer.serialize(message) try: self._socket.sendto(raw_message, (host, port)) except Exception as e: if self._cb_ignore_write_exception is not None and isinstance( self._cb_ignore_write_exception, collections.Callable): if not self._cb_ignore_write_exception(e, self): raise # if you're explicitly setting that you don't want a response, don't wait for it # https://tools.ietf.org/html/rfc7967#section-2.1 for opt in message.options: if opt.number == defines.OptionRegistry.NO_RESPONSE.number: if opt.value == 26: return if self._receiver_thread is None or not self._receiver_thread.is_alive( ): self._receiver_thread = threading.Thread( target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start() def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform( defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread = threading.Thread( target=self._retransmit, name=str('%s-Retry-%d' % (threading.current_thread().name, message.mid)), args=(transaction, message, future_time, 0)) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: logger.debug("retransmit loop ... enter") while retransmit_count <= defines.MAX_RETRANSMIT \ and (not message.acknowledged and not message.rejected) \ and not transaction.retransmit_stop.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not transaction.retransmit_stop.isSet( ): retransmit_count += 1 future_time *= 2 if retransmit_count < defines.MAX_RETRANSMIT: logger.debug("retransmit loop ... retransmit Request") self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format( message=message.line_print)) message.timeouted = True # Inform the user, that nothing was received self._callback(None) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None logger.debug("retransmit loop ... exit") def receive_datagram(self): """ Receive datagram from the UDP socket and invoke the callback function. """ logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(0.1) try: datagram, addr = self._socket.recvfrom(1500) except socket.timeout: # pragma: no cover continue except Exception as e: # pragma: no cover if self._cb_ignore_read_exception is not None and isinstance( self._cb_ignore_read_exception, collections.Callable): if self._cb_ignore_read_exception(e, self): continue return else: # pragma: no cover if len(datagram) == 0: logger.debug( "Exiting receiver Thread due to orderly shutdown on server end" ) return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): logger.debug("receive_datagram - " + str(message)) transaction, send_ack = self._messageLayer.receive_response( message) if transaction is None: # pragma: no cover continue self._wait_for_retransmit_thread(transaction) if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: self._send_block_request(transaction) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty( transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) logger.debug("Exiting receiver Thread due to request") def _send_ack(self, transaction): """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread(target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet(): logger.debug("retransmit Request") retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: print 'orderly shutdown on server end' return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response(message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request(transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover # Handle separate """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self._cb_ignore_listen_exception = cb_ignore_listen_exception addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock elif self.multicast: # pragma: no cover # Create a socket # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((defines.ALL_COAP_NODES, self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1])) addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0] group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: print 'orderly shutdown on server end' return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response( message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request( transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty( transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover # Handle separate """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): def __init__(self, server_address, multicast=False, starting_mid=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES, None)[0] group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES_IPV6, None)[0] group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) def purge(self): """ Clean old transactions """ while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) if len(client_address) > 2: client_address = (client_address[0], client_address[1]) except socket.timeout: continue try: serializer = Serializer() message = serializer.deserialize(data, client_address) if isinstance(message, int): logger.error("receive_datagram - BAD REQUEST") rst = Message() rst.destination = client_address rst.type = defines.Types["RST"] rst.code = message rst.mid = self._messageLayer._current_mid self._messageLayer._current_mid += 1 % 65535 self.send_datagram(rst) continue logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug( "message duplicated, transaction completed") if transaction.response is not None: self.send_datagram(transaction.response) continue elif transaction.request.duplicated and not transaction.completed: logger.debug( "message duplicated, transaction NOT completed") self._send_ack(transaction) continue args = (transaction, ) t = threading.Thread(target=self.receive_request, args=args) t.start() # self.receive_datagram(data, client_address) elif isinstance(message, Response): logger.error("Received response from %s", message.source) else: # is Message transaction = self._messageLayer.receive_empty(message) if transaction is not None: with transaction: self._blockLayer.receive_empty( message, transaction) self._observeLayer.receive_empty( message, transaction) except RuntimeError: print "Exception with Executor" self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_request(self, transaction): """ Receive datagram from the udp socket. :param data: the udp message :param client_address: the ip and port of the client """ with transaction: transaction.separate_timer = self._start_separate_timer( transaction) self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return self._observeLayer.receive_request(transaction) self._requestLayer.receive_request(transaction) if transaction.resource is not None and transaction.resource.changed: self.notify(transaction.resource) transaction.resource.changed = False elif transaction.resource is not None and transaction.resource.deleted: self.notify(transaction.resource) transaction.resource.deleted = False self._observeLayer.send_response(transaction) self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response) def send_datagram(self, message): """ :type message: Message :param message: """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) if self.multicast: self._unicast_socket.sendto(message, (host, port)) else: self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :param path: the path for the new created resource :type resource: Resource :param resource: the resource to be added """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform( defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread( target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet( ): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format( message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ Start a thread to handle separate mode. :type transaction: Transaction :param transaction: the transaction that is in processing :rtype : the Timer object """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction, )) t.start() return t @staticmethod def _stop_separate_timer(timer): """ Stop the separate Thread if an answer has been already provided to the client. :param timer: The Timer object """ timer.cancel() def _send_ack(self, transaction): """ Sends an ACK message for the request. :param transaction: the transaction that owns the request """ ack = Message() ack.type = defines.Types['ACK'] # TODO handle mutex on transaction if not transaction.request.acknowledged and transaction.request.type == defines.Types[ "CON"]: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): """ Notifies the observers of a certain resource. :param resource: the resource """ observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: with transaction: transaction.response = None transaction = self._requestLayer.receive_request(transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response)
def __init__(self, server_address, multicast=False, starting_mid=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES, None)[0] group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES_IPV6, None)[0] group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
class CoAP(object): def __init__(self, server_address, multicast=False, starting_mid=None): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() host, port = server_address ret = socket.getaddrinfo(host, port) family, socktype, proto, canonname, sockaddr = ret[0] self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast # IPv4 or IPv6 if len(sockaddr) == 4: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.multicast: # Set some options to make it multicast-friendly try: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except AttributeError: pass # Some systems don't support SO_REUSEPORT self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind to the port self._socket.bind(self.server_address) # Set some more multicast options interface = socket.gethostbyname(socket.gethostname()) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface)) self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address) + socket.inet_aton(interface)) else: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) def purge(self): while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) except socket.timeout: continue try: args = (data, client_address) t = threading.Thread(target=self.receive_datagram, args=args) t.start() # self.receive_datagram(data, client_address) except RuntimeError: print "Exception with Executor" self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_datagram(self, data, client_address): """ Receive datagram from the udp socket. :rtype : Message """ serializer = Serializer() message = serializer.deserialize(data, client_address) logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated,transaction completed") transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction.response.type = None transaction.request.acknowledged = False transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated,transaction NOT completed") self._send_ack(transaction) return transaction.separate_timer = self._start_separate_timer(transaction) transaction = self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return transaction = self._observeLayer.receive_request(transaction) transaction = self._requestLayer.receive_request(transaction) if transaction.resource is not None and transaction.resource.changed: self.notify(transaction.resource) transaction.resource.changed = False elif transaction.resource is not None and transaction.resource.deleted: self.notify(transaction.resource) transaction.resource.deleted = False transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response) elif isinstance(message, Message): transaction = self._messageLayer.receive_empty(message) if transaction is not None: transaction = self._blockLayer.receive_empty(message, transaction) transaction = self._observeLayer.receive_empty(message, transaction) else: # is Response logger.error("Received response from %s", message.source) def send_datagram(self, message): """ :type message: Message :param message: """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :type resource: Resource :param resource: """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retrasmission(self, transaction, message): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread(target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet(): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,)) t.start() return t @staticmethod def _stop_separate_timer(timer): """ :type future: Future :param future: """ timer.cancel() def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the request. :param request: [request, sleep_time] or request """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.request.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: transaction.response = None transaction = self._requestLayer.receive_request(transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response)
class CoAP(object): """ Implementation of the CoAP server """ def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self._cb_ignore_listen_exception = cb_ignore_listen_exception self._socketlist = [] addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock self._socketlist.append(sock) elif self.multicast: # pragma: no cover # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: # IPv6 self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self._socketlist.append(self._socket) ## multicast on OCF nodes : link local self._socket2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket2.bind(('', 5683)) # Allow messages from this socket to loop back for development try: self._socket2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) except: self._socket2.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_OCF_NODES_S3_IPV6), (chr(0) * 16).encode('utf-8')) #self._socket2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket2.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket2) ## multicast on OCF nodes: site local self._socket3 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket3.bind(('', 5683)) # Allow messages from this socket to loop back for development try: self._socket3.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) except: self._socket3.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_OCF_NODES_S5_IPV6), (chr(0) * 16).encode('utf-8')) try: self._socket3.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) except: self._socket3.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket3) ## multicast on ALL COAP nodes self._socket4 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket4.bind(('', 5683)) # Allow messages from this socket to loop back for development #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) self._socket4.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_COAP_NODES_IPV6_LINK), (chr(0) * 16).encode('utf-8')) #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket4.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket4) self._socket5 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket5.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket5.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket5.bind(('', 5683)) # Allow messages from this socket to loop back for development #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) self._socket5.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_COAP_NODES_IPV6_SITE), (chr(0) * 16).encode('utf-8')) #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket5.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket5) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self._socketlist.append(self._socket) def purge(self): """ Clean old transactions """ while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) empty = [] while not self.stopped.isSet(): try: data = None client_address = None readable, writable, exceptional = select.select(self._socketlist, empty, empty, timeout) for s in readable: data, client_address = s.recvfrom(4096) if len(client_address) > 2: client_address = (client_address[0], client_address[1]) print ("listen : received ", client_address) except socket.timeout: print (".") continue except Exception as e: if self._cb_ignore_listen_exception is not None and isinstance(self._cb_ignore_listen_exception, collections.Callable): if self._cb_ignore_listen_exception(e, self): continue raise try: serializer = Serializer() if data is not None: message = serializer.deserialize(data, client_address) if isinstance(message, int): logger.error("receive_datagram - BAD REQUEST") rst = Message() rst.destination = client_address rst.type = defines.Types["RST"] rst.code = message rst.mid = self._messageLayer.fetch_mid() self.send_datagram(rst) continue logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated, transaction completed") if transaction.response is not None: self.send_datagram(transaction.response) continue elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated, transaction NOT completed") self._send_ack(transaction) continue args = (transaction, ) t = threading.Thread(target=self.receive_request, args=args) t.start() # self.receive_datagram(data, client_address) elif isinstance(message, Response): logger.error("Received response from %s", message.source) else: # is Message transaction = self._messageLayer.receive_empty(message) if transaction is not None: with transaction: self._blockLayer.receive_empty(message, transaction) self._observeLayer.receive_empty(message, transaction) except RuntimeError: logger.exception("Exception with Executor") # close sockets for sock in self._socketlist: sock.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() def receive_request(self, transaction): """ Handle requests coming from the udp socket. :param transaction: the transaction created to manage the request """ with transaction: transaction.separate_timer = self._start_separate_timer(transaction) self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return self._observeLayer.receive_request(transaction) self._requestLayer.receive_request(transaction) if transaction.resource is not None and transaction.resource.changed: self.notify(transaction.resource) transaction.resource.changed = False elif transaction.resource is not None and transaction.resource.deleted: self.notify(transaction.resource) transaction.resource.deleted = False self._observeLayer.send_response(transaction) self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response) def send_datagram(self, message): """ Send a message through the udp socket. :type message: Message :param message: the message to send """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :param path: the path for the new created resource :type resource: Resource :param resource: the resource to be added """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: resource.path = actual_path self.root[actual_path] = resource return True def remove_resource(self, path): """ Helper function to remove resources. :param path: the path for the unwanted resource :rtype : the removed object """ path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is not None: del(self.root[actual_path]) return res def _start_retransmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread(target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): if transaction.retransmit_stop is not None: transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet(): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ Start a thread to handle separate mode. :type transaction: Transaction :param transaction: the transaction that is in processing :rtype : the Timer object """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,)) t.start() return t @staticmethod def _stop_separate_timer(timer): """ Stop the separate Thread if an answer has been already provided to the client. :param timer: The Timer object """ timer.cancel() def _send_ack(self, transaction): """ Sends an ACK message for the request. :param transaction: the transaction that owns the request """ ack = Message() ack.type = defines.Types['ACK'] # TODO handle mutex on transaction if not transaction.request.acknowledged and transaction.request.type == defines.Types["CON"]: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): """ Notifies the observers of a certain resource. :param resource: the resource """ observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: with transaction: transaction.response = None transaction = self._requestLayer.receive_request(transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response)
def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self._cb_ignore_listen_exception = cb_ignore_listen_exception self._socketlist = [] addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock self._socketlist.append(sock) elif self.multicast: # pragma: no cover # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: # IPv6 self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self._socketlist.append(self._socket) ## multicast on OCF nodes : link local self._socket2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket2.bind(('', 5683)) # Allow messages from this socket to loop back for development try: self._socket2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) except: self._socket2.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_OCF_NODES_S3_IPV6), (chr(0) * 16).encode('utf-8')) #self._socket2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket2.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket2) ## multicast on OCF nodes: site local self._socket3 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket3.bind(('', 5683)) # Allow messages from this socket to loop back for development try: self._socket3.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) except: self._socket3.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_OCF_NODES_S5_IPV6), (chr(0) * 16).encode('utf-8')) try: self._socket3.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) except: self._socket3.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket3) ## multicast on ALL COAP nodes self._socket4 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket4.bind(('', 5683)) # Allow messages from this socket to loop back for development #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) self._socket4.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_COAP_NODES_IPV6_LINK), (chr(0) * 16).encode('utf-8')) #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket4.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket4) self._socket5 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket5.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket5.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket5.bind(('', 5683)) # Allow messages from this socket to loop back for development #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) self._socket5.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_COAP_NODES_IPV6_SITE), (chr(0) * 16).encode('utf-8')) #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket5.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket5) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self._socketlist.append(self._socket)
def __init__(self, server_address, multicast=False, starting_mid=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0]) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) print "Yes Socket IPV6 is " print self.server_address self._socket.bind(self.server_address)