async def send(self, bytes_to_send): """Sends data.""" if self.address is None: raise ca.CaprotoNetworkError( "Cannot send on a disconnected UDP transport") try: self.transport.send(bytes_to_send) except OSError as exc: raise ca.CaprotoNetworkError( f"Failed to send to {self.address[0]}:{self.address[1]}" ) from exc
async def send(self, bytes_to_send): try: self.transport.sendto(bytes_to_send, self.address) except OSError as exc: host, port = self.address raise ca.CaprotoNetworkError( f"Failed to send to {host}:{port}") from exc
async def send(self, port, *commands): """ Process a command and tranport it over the UDP socket. """ bytes_to_send = self.broadcaster.send(*commands) tags = { 'role': 'CLIENT', 'our_address': self.broadcaster.client_address, 'direction': '--->>>' } for host in ca.get_address_list(): if ':' in host: host, _, port_as_str = host.partition(':') specified_port = int(port_as_str) else: specified_port = port tags['their_address'] = (host, specified_port) self.broadcaster.log.debug('%d commands %dB', len(commands), len(bytes_to_send), extra=tags) try: await self.udp_sock.sendto(bytes_to_send, (host, specified_port)) except OSError as ex: raise ca.CaprotoNetworkError( f'{ex} while sending {len(bytes_to_send)} bytes to ' f'{host}:{specified_port}') from ex
async def sendto(self, bytes_to_send, address): """Sends data to address.""" try: self.transport.sendto(bytes_to_send, address) except OSError as exc: raise ca.CaprotoNetworkError( f"Failed to send to {self.address[0]}:{self.address[1]}" ) from exc
def send_search(): for dest in client_address_list: tags['their_address'] = dest b.log.debug( '%d commands %dB', len(commands), len(bytes_to_send), extra=tags) try: udp_sock.sendto(bytes_to_send, dest) except OSError as exc: host, port = dest raise ca.CaprotoNetworkError(f"Failed to send to {host}:{port}") from exc
def send_search(): for host in ca.get_address_list(): if ':' in host: host, _, specified_port = host.partition(':') dest = (host, int(specified_port)) else: dest = (host, CA_SERVER_PORT) try: udp_sock.sendto(bytes_to_send, dest) except OSError as exc: host, port = dest raise ca.CaprotoNetworkError(f"Failed to send to {host}:{port}") from exc logger.debug('Search request sent to %r.', dest)
async def send(self, bytes_to_send): """Sends data over a connected socket.""" try: async with self.lock: self.writer.write(bytes_to_send) await self.writer.drain() except OSError as exc: try: host, port = self.getpeername() except Exception: destination = '' else: destination = f' to {host}:{port}' raise ca.CaprotoNetworkError( f"Failed to send{destination}") from exc
def send_search(): for host in ca.get_address_list(): if ':' in host: host, _, specified_port = host.partition(':') dest = (host, int(specified_port)) else: dest = (host, CA_SERVER_PORT) tags['their_address'] = dest b.log.debug('%d commands %dB', len(commands), len(bytes_to_send), extra=tags) try: udp_sock.sendto(bytes_to_send, dest) except OSError as exc: host, port = dest raise ca.CaprotoNetworkError( f"Failed to send to {host}:{port}") from exc
async def send(self, port, *commands): """ Process a command and tranport it over the UDP socket. """ bytes_to_send = self.broadcaster.send(*commands) for host in ca.get_address_list(): if ':' in host: host, _, port_as_str = host.partition(':') specified_port = int(port_as_str) else: specified_port = port self.broadcaster.log.debug('Sending %d bytes to %s:%d', len(bytes_to_send), host, specified_port) try: await self.udp_sock.sendto(bytes_to_send, (host, specified_port)) except OSError as ex: raise ca.CaprotoNetworkError( f'{ex} while sending {len(bytes_to_send)} bytes to ' f'{host}:{specified_port}') from ex
def search(pv_name, udp_sock, timeout, *, max_retries=2): # Set Broadcaster log level to match our logger. b = ca.Broadcaster(our_role=ca.CLIENT) b.client_address = safe_getsockname(udp_sock) # Send registration request to the repeater logger.debug('Registering with the Channel Access repeater.') bytes_to_send = b.send(ca.RepeaterRegisterRequest()) env = get_environment_variables() repeater_port = env['EPICS_CA_REPEATER_PORT'] client_address_list = ca.get_client_address_list() local_address = ca.get_local_address() try: udp_sock.sendto(bytes_to_send, (local_address, repeater_port)) except OSError as exc: raise ca.CaprotoNetworkError( f"Failed to send to {local_address}:{repeater_port}") from exc logger.debug("Searching for %r....", pv_name) commands = (ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION), ca.SearchRequest(pv_name, 0, ca.DEFAULT_PROTOCOL_VERSION)) bytes_to_send = b.send(*commands) tags = { 'role': 'CLIENT', 'our_address': b.client_address, 'direction': '--->>>' } def send_search(): for dest in client_address_list: tags['their_address'] = dest b.log.debug('%d commands %dB', len(commands), len(bytes_to_send), extra=tags) try: udp_sock.sendto(bytes_to_send, dest) except OSError as exc: host, port = dest raise ca.CaprotoNetworkError( f"Failed to send to {host}:{port}") from exc def check_timeout(): nonlocal retry_at if time.monotonic() >= retry_at: send_search() retry_at = time.monotonic() + retry_timeout if time.monotonic() - t > timeout: raise CaprotoTimeoutError( f"Timed out while awaiting a response " f"from the search for {pv_name!r}. Search " f"requests were sent to this address list: " f"{ca.get_address_list()}.") # Initial search attempt send_search() # Await a search response, and keep track of registration status retry_timeout = timeout / max((max_retries, 1)) t = time.monotonic() retry_at = t + retry_timeout try: orig_timeout = udp_sock.gettimeout() udp_sock.settimeout(retry_timeout) while True: try: bytes_received, address = udp_sock.recvfrom(ca.MAX_UDP_RECV) except socket.timeout: check_timeout() continue check_timeout() commands = b.recv(bytes_received, address) b.process_commands(commands) for command in commands: if isinstance(command, ca.SearchResponse) and command.cid == 0: address = ca.extract_address(command) logger.debug('Found %r at %s:%d', pv_name, *address) return address else: # None of the commands we have seen are a reply to our request. # Receive more data. continue finally: udp_sock.settimeout(orig_timeout)
def _run_repeater(server_sock, bind_addr): 'Run the repeater using server_socket and bind_address' bind_host, bind_port = bind_addr servers = {} clients = {} broadcaster = caproto.Broadcaster(our_role=caproto.SERVER) logger.info("Repeater is listening on %s:%d", bind_host, bind_port) while True: msg, addr = server_sock.recvfrom(MAX_UDP_RECV) host, port = addr if port in clients and clients[port] != host: # broadcast only from one interface continue elif (port in servers and servers[port]['host'] != host): continue try: commands = broadcaster.recv(msg, addr) broadcaster.process_commands(commands) except Exception: logger.exception('Failed to process incoming datagram') continue if not commands: # NOTE: additional valid way of registration is an empty # message, according to broadcaster source if port not in clients: clients[port] = host logger.debug('New client %s (zero-length registration)', addr) continue to_forward = [] for command in commands: if isinstance(command, caproto.Beacon): # Update our records of the last time each server checked in # (i.e. issued a heartbeat). servers[command.server_port] = dict(up_at=time.time(), host=host) # The sender of this command may leave the IP field empty (0), # leaving it up to the repeater to fill in the address so that # the ultimate recipient knows the correct origin. By leaving # that up to the repeater, the sender avoids providing the # wrong return address (i.e. picking the wrong interface). It # is safer to let the repeater determine the return address # by inspection. if command.header.parameter2 == 0: updated_ip = caproto.ipv4_to_int32(host) command.header.parameter2 = updated_ip to_forward.append(command) elif isinstance(command, caproto.RepeaterRegisterRequest): if port not in clients: clients[port] = host logger.debug('New client %s', addr) confirmation_bytes = broadcaster.send( caproto.RepeaterConfirmResponse(host)) try: server_sock.sendto(confirmation_bytes, (host, port)) except OSError as exc: raise caproto.CaprotoNetworkError( f"Failed to send to {host}:{port}") from exc remove_clients = list(check_clients(clients, skip=port)) _update_all(clients, servers, remove_clients=remove_clients) # Do not broadcast registration requests to other clients. # Omit it from `to_forward`. else: to_forward.append(command) bytes_to_broadcast = b''.join(bytes(cmd) for cmd in to_forward) to_remove = [] for other_port, other_host in clients.items(): if other_port != port: try: server_sock.sendto(bytes_to_broadcast, (other_host, other_port)) except Exception: to_remove.append((other_host, other_port)) if to_remove: _update_all(clients, servers, remove_clients=to_remove)
async def recv(self, nbytes=4096): """Receive from the socket.""" try: return await self.reader.read(nbytes) except OSError as exc: raise ca.CaprotoNetworkError(f"Failed to receive: {exc}") from exc
def search(pv_name, udp_sock, timeout, *, max_retries=2): # Set Broadcaster log level to match our logger. b = ca.Broadcaster(our_role=ca.CLIENT) # Send registration request to the repeater logger.debug('Registering with the Channel Access repeater.') bytes_to_send = b.send(ca.RepeaterRegisterRequest()) repeater_port = os.environ.get('EPICS_CA_REPEATER_PORT', 5065) for host in ca.get_address_list(): try: udp_sock.sendto(bytes_to_send, (host, repeater_port)) except OSError as exc: raise ca.CaprotoNetworkError( f"Failed to send to {host}:{repeater_port}") from exc logger.debug("Searching for '%s'....", pv_name) bytes_to_send = b.send( ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION), ca.SearchRequest(pv_name, 0, ca.DEFAULT_PROTOCOL_VERSION)) def send_search(): for host in ca.get_address_list(): if ':' in host: host, _, specified_port = host.partition(':') dest = (host, int(specified_port)) else: dest = (host, CA_SERVER_PORT) try: udp_sock.sendto(bytes_to_send, dest) except OSError as exc: host, port = dest raise ca.CaprotoNetworkError( f"Failed to send to {host}:{port}") from exc logger.debug('Search request sent to %r.', dest) def check_timeout(): nonlocal retry_at if time.monotonic() >= retry_at: send_search() retry_at = time.monotonic() + retry_timeout if time.monotonic() - t > timeout: raise CaprotoTimeoutError( f"Timed out while awaiting a response " f"from the search for {pv_name!r}. Search " f"requests were sent to this address list: " f"{ca.get_address_list()}.") # Initial search attempt send_search() # Await a search response, and keep track of registration status retry_timeout = timeout / max((max_retries, 1)) t = time.monotonic() retry_at = t + retry_timeout try: orig_timeout = udp_sock.gettimeout() udp_sock.settimeout(retry_timeout) while True: try: bytes_received, address = udp_sock.recvfrom(ca.MAX_UDP_RECV) except socket.timeout: check_timeout() continue check_timeout() commands = b.recv(bytes_received, address) b.process_commands(commands) for command in commands: if isinstance(command, ca.SearchResponse) and command.cid == 0: address = ca.extract_address(command) logger.debug('Found %s at %s', pv_name, address) return address else: # None of the commands we have seen are a reply to our request. # Receive more data. continue finally: udp_sock.settimeout(orig_timeout)