diff --git a/ros2cli/ros2cli/daemon/__init__.py b/ros2cli/ros2cli/daemon/__init__.py
index 42c5affdf..1c00795bf 100644
--- a/ros2cli/ros2cli/daemon/__init__.py
+++ b/ros2cli/ros2cli/daemon/__init__.py
@@ -151,6 +151,13 @@ def shutdown_handler():
             pass
 
 
+def serve_and_close(server: LocalXMLRPCServer, *, timeout: int = 2 * 60 * 60):
+    try:
+        serve(server, timeout=timeout)
+    finally:
+        server.server_close()
+
+
 def main(*, argv=None):
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter)
diff --git a/ros2cli/ros2cli/node/daemon.py b/ros2cli/ros2cli/node/daemon.py
index c25a2d847..43342b4cf 100644
--- a/ros2cli/ros2cli/node/daemon.py
+++ b/ros2cli/ros2cli/node/daemon.py
@@ -46,7 +46,7 @@ def connected(self):
                 for method in self._proxy.system.listMethods()
                 if not method.startswith('system.')
             ]
-        except ConnectionRefusedError:
+        except (ConnectionRefusedError, ConnectionResetError):
             return False
         return True
 
@@ -168,7 +168,7 @@ def spawn_daemon(args, timeout=None, debug=False):
             'rmw_implementation': rclpy.get_rmw_implementation_identifier()}
 
         daemonize(
-            functools.partial(daemon.serve, server),
+            functools.partial(daemon.serve_and_close, server),
             tags=tags, timeout=timeout, debug=debug)
     finally:
         server.server_close()
diff --git a/ros2cli/ros2cli/xmlrpc/local_server.py b/ros2cli/ros2cli/xmlrpc/local_server.py
index 9e3e9e476..abe14a953 100644
--- a/ros2cli/ros2cli/xmlrpc/local_server.py
+++ b/ros2cli/ros2cli/xmlrpc/local_server.py
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
 import socket
-import struct
 # Import SimpleXMLRPCRequestHandler to re-export it.
 from xmlrpc.server import SimpleXMLRPCRequestHandler  # noqa
 from xmlrpc.server import SimpleXMLRPCServer
@@ -32,20 +32,12 @@ def get_local_ipaddrs():
 
 class LocalXMLRPCServer(SimpleXMLRPCServer):
 
-    allow_reuse_address = False
-
-    def server_bind(self):
-        # Prevent listening socket from lingering in TIME_WAIT state after close()
-        self.socket.setsockopt(
-            socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
-        super(LocalXMLRPCServer, self).server_bind()
-
-    def get_request(self):
-        # Prevent accepted socket from lingering in TIME_WAIT state after close()
-        sock, addr = super(LocalXMLRPCServer, self).get_request()
-        sock.setsockopt(
-            socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
-        return sock, addr
+    # Allow re-binding even if another server instance was recently bound (i.e. we are still in
+    # TCP TIME_WAIT). This is already the default behavior on Windows, and further SO_REUSEADDR can
+    # lead to undefined behavior on Windows; see
+    # https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse.  # noqa
+    # So we don't set the option for Windows.
+    allow_reuse_address = False if os.name == 'nt' else True
 
     def verify_request(self, request, client_address):
         if client_address[0] not in get_local_ipaddrs():