解决切换WiFi以后 SSDP无法扫描到的问题

This commit is contained in:
张开科 2025-06-19 08:48:17 +08:00
parent 78c990a114
commit d3561dae4f

View File

@ -138,8 +138,7 @@ class ZeroconfRegistrar:
name = f"{instance_name} ({host})"
if len(name) > 64:
name = instance_name
await self.ssdp_server.start()
self.ssdp_server.register_service(name, addr, hi["port"])
await self.ssdp_server.start(addr, name, hi["port"])
async def close(self) -> None:
await self.runner.unregister_services([self.service_info])
@ -151,6 +150,14 @@ class ZeroconfRegistrar:
addresses = [x for x in self._extract_ip_addresses(network)]
self.service_info.addresses = addresses
await self.runner.update_services([self.service_info])
if self.ssdp_server is not None:
machine: Machine = self.server.lookup_component("machine")
addr = machine.public_ip
if not addr:
addr = f"{self.mdns_name}.local"
name = self.ssdp_server.name
hi = self.server.get_host_info()
await self.ssdp_server.update_service(addr, name, hi["port"])
def _extract_ip_addresses(self, network: Dict[str, Any]) -> Iterator[bytes]:
for ifname, ifinfo in network.items():
@ -208,6 +215,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
self.running: bool = False
self.close_fut: Optional[asyncio.Future] = None
self.response_handle: Optional[asyncio.TimerHandle] = None
self.transport: Optional[asyncio.DatagramTransport] = None
eventloop = self.server.get_event_loop()
self.boot_id = int(eventloop.get_loop_time())
self.config_id = 1
@ -242,20 +250,22 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, ip_combo)
return sock
async def start(self) -> None:
async def start(self, addr: str, name: str, port: int) -> None:
if self.running:
return
await self.stop()
try:
sock = self._create_ssdp_socket()
sock = self._create_ssdp_socket(source_addr=(addr, 0))
sock.settimeout(0)
sock.setblocking(False)
sock.bind(("", SSDP_ADDR[1]))
_loop = asyncio.get_running_loop()
ret = await _loop.create_datagram_endpoint(lambda: self, sock=sock)
self.transport, _ = ret
except (socket.error, OSError):
self.running = True
self.register_service(name, addr, port)
except (socket.error, OSError) as e:
logging.error(f"Failed to start SSDP server: {e}")
return
self.running = True
async def stop(self) -> None:
if not self.running:
@ -265,18 +275,22 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
if self.response_handle is not None:
self.response_handle.cancel()
self.response_handle = None
if self.transport.is_closing():
logging.info("Transport already closing")
return
for notification in self._build_notifications("ssdp:byebye"):
self.transport.sendto(notification, SSDP_ADDR)
self.close_fut = self.server.get_event_loop().create_future()
self.transport.close()
try:
await asyncio.wait_for(self.close_fut, 2.)
except asyncio.TimeoutError:
pass
self.close_fut = None
if self.transport is not None and not self.transport.is_closing():
for notification in self._build_notifications("ssdp:byebye"):
self.transport.sendto(notification, SSDP_ADDR)
self.close_fut = self.server.get_event_loop().create_future()
self.transport.close()
try:
await asyncio.wait_for(self.close_fut, 2.)
except asyncio.TimeoutError:
logging.warning("Timeout waiting for SSDP transport to close")
self.close_fut = None
self.transport = None
async def update_service(self, addr: str, name: str, port: int) -> None:
if self.running:
await self.stop()
await self.start(addr, name, port)
def register_service(
self, name: str, host_name_or_ip: str, port: int
@ -292,7 +306,6 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
model = self.name.rsplit("-", 1)[0]
if "(" in self.name and ")" in self.name:
device_name = self.name.split("(", 1)[1].split(")", 1)[0]
app: MoonrakerApp = self.server.lookup_component("application")
self.base_url = f"http://{host_name_or_ip}"
self.response_headers = [
f"USN: uuid:{self.serial_number}::upnp:rootdevice::urn:creatbot-com:device:3dprinter:2",
@ -308,7 +321,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
]
self.registered = True
advertisements = self._build_notifications("ssdp:alive")
if self.running:
if self.running and self.transport is not None:
for ad in advertisements:
self.transport.sendto(ad, SSDP_ADDR)
self.advertisements = cycle(advertisements)
@ -335,7 +348,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
)
def _advertise_presence(self, eventtime: float) -> float:
if self.running and self.registered:
if self.running and self.registered and self.transport is not None:
cur_ad = next(self.advertisements)
self.transport.sendto(cur_ad, SSDP_ADDR)
delay = random.uniform(SSDP_MAX_AGE / 6., SSDP_MAX_AGE / 3.)
@ -344,6 +357,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
def connection_made(
self, transport: asyncio.transports.BaseTransport
) -> None:
self.transport = transport
logging.debug("SSDP Server Connected")
def connection_lost(self, exc: Exception | None) -> None:
@ -396,7 +410,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
self._respond_to_discovery(addr)
def _respond_to_discovery(self, addr: tuple[str | Any, int]) -> None:
if not self.running:
if not self.running or self.transport is None:
return
self.response_handle = None
response: List[str] = ["HTTP/1.1 200 OK"]