async def attempt_connect(nursery, previous_attempt_failed): # Wait until either the previous attempt failed, or the timeout # expires (unless this is the first invocation, in which case we just # go ahead). if previous_attempt_failed is not None: with trio.move_on_after(happy_eyeballs_delay): await previous_attempt_failed.wait() # Claim our target. try: *socket_args, _, target_sockaddr = next(targets_iter) except StopIteration: return # Then kick off the next attempt. this_attempt_failed = trio.Event() nursery.start_soon(attempt_connect, nursery, this_attempt_failed) # Then make this invocation's attempt try: with close_on_error(socket(*socket_args)) as sock: await sock.connect(target_sockaddr) except OSError as exc: # This connection attempt failed, but the next one might # succeed. Save the error for later so we can report it if # everything fails, and tell the next attempt that it should go # ahead (if it hasn't already). oserrors.append(exc) this_attempt_failed.set() else: # Success! Save the winning socket and cancel all outstanding # connection attempts. winning_sockets.append(sock) nursery.cancel_scope.cancel()
async def init_conn(bridge, iface=""): *sarg, _, rbridge = (await socket.getaddrinfo(*bridge, type=socket.SOCK_STREAM))[0] retry = 3 for _ in range(retry): sa = socket.socket(*sarg) sa.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) await sa.connect(rbridge) priv_host, priv_port = sa.getsockname() # self, private if iface: priv_host, priv_port = get_priv_host(iface), 0 try: s_cona = await bound_socket( (priv_host, priv_port)) # for connect, a if iface: _, priv_port = s_cona.getsockname() s_conb = await bound_socket( (priv_host, priv_port)) # for connect, b s_acp = await bound_socket(('', priv_port)) # for accept break except OSError: # NOTE: albeit SO_REUSEADDR set, bind still occassionally fails logger.debug( f"cannot bind to private addr {priv_host}:{priv_port}, retry") sa.close() await trio.sleep(0.1) continue else: raise OSError("failed to bind to a local address") logger.debug(f"self's private addr {priv_host}:{priv_port}") return trio.SocketStream( sa), f"{priv_host}:{priv_port}", s_acp, s_cona, s_conb
async def open_unix_socket(filename): """Opens a connection to the specified `Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`__. You must have read/write permission on the specified file to connect. Args: filename (str or bytes): The filename to open the connection to. Returns: SocketStream: a :class:`~trio.abc.Stream` connected to the given file. Raises: OSError: If the socket file could not be connected to. RuntimeError: If AF_UNIX sockets are not supported. """ if not has_unix: raise RuntimeError("Unix sockets are not supported on this platform") # much more simplified logic vs tcp sockets - one socket type and only one # possible location to connect to sock = socket(AF_UNIX, SOCK_STREAM) with close_on_error(sock): await sock.connect(os.fspath(filename)) return trio.SocketStream(sock)
async def get_socket_port(server): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) await s.bind((server, 0)) s.listen(1) port = s.getsockname()[1] s.close() return port
async def bound_socket(local_addr): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if hasattr(socket, "SO_REUSEPORT"): s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) await s.bind(local_addr) logger.debug(f"socket bound to {local_addr} created") return s
async def _serve(listen_address): log_util.set_listen_address(listen_address) with socket.socket(family=get_ip_family(listen_address), type=socket.SOCK_DGRAM) as udp_sock: await udp_sock.bind(listen_address) logger.info('Started listening') await run_accept_loop(udp_sock, resource_id, encoders)
async def _create_socket(self): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) await self._sock.bind((self.multicast_group, self.port)) mreq = struct.pack("4sl", socket.inet_aton(self.multicast_group), socket.INADDR_ANY) self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
async def main_loop(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) await self.sock.bind((interface['addr'], ARTNET_PORT)) #nodes = [ node async for node in search_nodes(self.sock, self.interface) ] #print(nodes) while True: #I DONT KNOW WHAT TO DO UNTIL I HAVE A FRAMEWORK TO FRAME MY WORK AROUND pass
async def serve(host, port, stream_changes, allow_remote_class_registration, allow_import_from_main, max_queue_size): # todo: catch and send back errors (ignoring socket closing errors?) global MAX_QUEUE_SIZE MAX_QUEUE_SIZE = max_queue_size thread_executor = ThreadExecutor() executor = ProcessSocketServer(executor=thread_executor) executor.stream_changes = stream_changes executor.allow_remote_class_registration = allow_remote_class_registration executor.allow_import_from_main = allow_import_from_main sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (host, port) await sock.bind(server_address) sock.listen() async def handler(stream: SocketStream): data = await executor.read_decode_json_buffers(stream) if data.get('eof', False): # todo: close in a nicer way by not accepting new requests nursery.cancel_scope.cancel() return channel = data.get('stream', None) try: if channel is None: await socket_handler(executor, stream) elif channel == 'data': await socket_data_stream_handler(executor, stream, data['data']) else: await socket_stream_handler(executor, stream, channel, data['data']) except BrokenResourceError: # if channel is None, all errors are caught and sent to client. # Otherwise, and errors when channel != None are a client socket # issue so we can just drop it pass with ExecutorContext(thread_executor): async with thread_executor: async with trio.open_nursery() as nursery: nursery.start_soon(serve_listeners, handler, [SocketListener(sock)])
async def run_receive_loop(connection_opened, connection_closed, write_blocks, resource_id, connection_config): with socket.socket(family=get_ip_family(connection_config.address), type=socket.SOCK_DGRAM) as udp_sock: async with trio.open_nursery() as nursery: child_nursery, shutdown_trigger = await spawn_child_nursery(nursery.start_soon, shutdown_timeout=3) with trio.CancelScope() as cancel_scope: await udp_sock.connect(connection_config.address) log_util.set_listen_address(udp_sock.getsockname()[:2]) log_util.set_remote_address(connection_config.address) @once def shutdown(): # coordinate shutdown with higher order protocol instance connection_closed() # trigger child nursery timeout shutdown_trigger.set() # cancel receive loop cancel_scope.cancel() logger.debug('Closed connection') spawn = child_nursery.start_soon async def send(packet_to_send): packet_bytes = packet_to_send.to_bytes() await udp_sock.send(packet_bytes) connection = ClientSideConnection(shutdown, spawn, send, write_blocks, resource_id, connection_config.sending_rate, connection_config.reverse) # coordinate startup with higher order protocol instance connection_opened(connection) while True: try: data = await udp_sock.recv(2048) packet = PacketType.parse_packet(data) except (ConnectionResetError, ConnectionRefusedError): # maybe handle this error # however, it is not guaranteed that we will receive an error when sending into the void pass except ValueError as exc: logger.exception(exc) else: await connection.handle_packet(packet)
async def attempt_connect(socket_args, sockaddr, attempt_failed): nonlocal winning_socket try: sock = socket(*socket_args) open_sockets.add(sock) await sock.connect(sockaddr) # Success! Save the winning socket and cancel all outstanding # connection attempts. winning_socket = sock nursery.cancel_scope.cancel() except OSError as exc: # This connection attempt failed, but the next one might # succeed. Save the error for later so we can report it if # everything fails, and tell the next attempt that it should go # ahead (if it hasn't already). oserrors.append(exc) attempt_failed.set()
async def serve53(addr, resolve, task_status=trio.TASK_STATUS_IGNORED): with socket(AF_INET6 if ":" in addr[0] else AF_INET, SOCK_DGRAM) as sock: try: sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1) await sock.bind(addr) print(f"[Serve53] listening on {addr}") except OSError as e: if e.errno == 13: reason = "permission denied (run with sudo?)" elif e.errno in (48, 49): reason = "already in use (is another DNS server running?)" else: reason = str(e) print(f"[Serve53] {addr} {reason}") return finally: task_status.started() async with trio.open_nursery() as resolvers: while True: data, addr = await sock.recvfrom(8192) resolvers.start_soon(_process, sock, resolve, data, addr)
async def _run_servers(self): import trio print('hello') listeners = await trio.open_tcp_listeners(self.port) if self.unix_socket: from trio.socket import socket, SOCK_STREAM, AF_UNIX, SOL_SOCKET, SO_REUSEADDR sock = socket(AF_UNIX, SOCK_STREAM) sock.setsockopt( SOL_SOCKET, SO_REUSEADDR, 1 ) await sock.bind(self.unix_socket) sock.listen(10) listeners.append(trio.SocketListener(sock)) print(listeners) await trio.serve_listeners(self.handle_client, listeners)
async def open_socket(self) -> SocketStream: """Opens socket, sends channel=None, reads the response and returns the socket stream. :return: """ data = self.encode({'stream': None}) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (self.server, self.port) await sock.connect(server_address) stream = SocketStream(sock) try: await self.write_socket(data, stream) await self.read_decode_json_buffers(stream) except Exception: await stream.aclose() raise return stream
async def _init(self): family, socktype, proto, canonname, sockaddr = ( await socket.getaddrinfo( None if self.multicast else self.host, self.port, socket.AF_INET6 if self.ipv6 else socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE, ))[0] self._sock = socket.socket(family, socktype, proto) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) await self._sock.bind(sockaddr) if self.multicast: if hasattr(socket, "SO_REUSEPORT"): self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) val = None if family == socket.AF_INET: assert "." in self.host val = struct.pack("4sl", socket.inet_aton(self.host), socket.INADDR_ANY) elif family == socket.AF_INET6: raise NotImplementedError("IPv6 support not ready yet") else: raise ValueError("Unsupported network address family") self._sock.setsockopt( socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, val, ) self._sock.setsockopt( socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0, )
async def start_executor(self): if self._process is not None: raise TypeError('Executor already started') port = self.port if not port: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) await s.bind((self.server, 0)) s.listen(1) port = self.port = s.getsockname()[1] s.close() env = os.environ.copy() env['KIVY_NO_ARGS'] = '1' self._process = await trio.open_process([ sys.executable, '-m', 'pymoa_remote.app.multiprocessing', '--host', str(self.server), '--port', str(port), '--stream_changes', str(self.stream_changes), '--remote_class_registration', str(self.allow_remote_class_registration), '--import_from_main', str(self.allow_import_from_main) ], env=env) # wait until the process is ready ts = time.perf_counter() while True: try: async with self.create_socket_context(): break except OSError: if time.perf_counter() - ts >= 5: raise await trio.sleep(.01) await super(MultiprocessSocketExecutor, self).start_executor()
async def attempt_connect(socket_args, sockaddr, attempt_failed): nonlocal winning_socket try: sock = socket(*socket_args) open_sockets.add(sock) if local_address is not None: # TCP connections are identified by a 4-tuple: # # (local IP, local port, remote IP, remote port) # # So if a single local IP wants to make multiple connections # to the same (remote IP, remote port) pair, then those # connections have to use different local ports, or else TCP # won't be able to tell them apart. OTOH, if you have multiple # connections to different remote IP/ports, then those # connections can share a local port. # # Normally, when you call bind(), the kernel will immediately # assign a specific local port to your socket. At this point # the kernel doesn't know which (remote IP, remote port) # you're going to use, so it has to pick a local port that # *no* other connection is using. That's the only way to # guarantee that this local port will be usable later when we # call connect(). (Alternatively, you can set SO_REUSEADDR to # allow multiple nascent connections to share the same port, # but then connect() might fail with EADDRNOTAVAIL if we get # unlucky and our TCP 4-tuple ends up colliding with another # unrelated connection.) # # So calling bind() before connect() works, but it disables # sharing of local ports. This is inefficient: it makes you # more likely to run out of local ports. # # But on some versions of Linux, we can re-enable sharing of # local ports by setting a special flag. This flag tells # bind() to only bind the IP, and not the port. That way, # connect() is allowed to pick the the port, and it can do a # better job of it because it knows the remote IP/port. try: sock.setsockopt(trio.socket.IPPROTO_IP, trio.socket.IP_BIND_ADDRESS_NO_PORT, 1) except (OSError, AttributeError): pass try: await sock.bind((local_address, 0)) except OSError: raise OSError( f"local_address={local_address!r} is incompatible " f"with remote address {sockaddr}") await sock.connect(sockaddr) # Success! Save the winning socket and cancel all outstanding # connection attempts. winning_socket = sock nursery.cancel_scope.cancel() except OSError as exc: # This connection attempt failed, but the next one might # succeed. Save the error for later so we can report it if # everything fails, and tell the next attempt that it should go # ahead (if it hasn't already). oserrors.append(exc) attempt_failed.set()