diff --git a/python/requirements.txt b/python/requirements.txt
index 9d53b550cd..cb48193733 100644
--- a/python/requirements.txt
+++ b/python/requirements.txt
@@ -4,3 +4,4 @@ cython
 pandas
 scipy
 setuptools>=60.0.0
+telnetlib3
diff --git a/src/input_output/FGInputSocket.cpp b/src/input_output/FGInputSocket.cpp
index 89de037d7f..eb7de7b1cf 100644
--- a/src/input_output/FGInputSocket.cpp
+++ b/src/input_output/FGInputSocket.cpp
@@ -259,7 +259,7 @@ void FGInputSocket::Read(bool Holding)
       } else if (command == "help") {               // HELP
 
         socket->Reply(
-        " JSBSim Server commands:\n\r\n"
+        " JSBSim Server commands:\r\n\r\n"
         "   get {property name}\r\n"
         "   set {property name} {value}\r\n"
         "   hold\r\n"
@@ -267,7 +267,7 @@ void FGInputSocket::Read(bool Holding)
         "   iterate {value}\r\n"
         "   help\r\n"
         "   quit\r\n"
-        "   info\n\r\n");
+        "   info\r\n\r\n");
 
       } else {
         socket->Reply(string("Unknown command: ") + command + "\r\n");
diff --git a/src/input_output/FGfdmSocket.cpp b/src/input_output/FGfdmSocket.cpp
index 536ae49367..535b3d10a1 100644
--- a/src/input_output/FGfdmSocket.cpp
+++ b/src/input_output/FGfdmSocket.cpp
@@ -269,7 +269,7 @@ string FGfdmSocket::Receive(void)
         int flags = fcntl(sckt_in, F_GETFL, 0);
         fcntl(sckt_in, F_SETFL, flags | O_NONBLOCK);
 #endif
-        if (send(sckt_in, "Connected to JSBSim server\n\rJSBSim> ", 36, 0) == SOCKET_ERROR)
+        if (send(sckt_in, "Connected to JSBSim server\r\nJSBSim> ", 36, 0) == SOCKET_ERROR)
           LogSocketError("Receive - TCP connection acknowledgement");
       }
     }
diff --git a/tests/TestInputSocket.py b/tests/TestInputSocket.py
index c4762ac07e..32dd6a109b 100644
--- a/tests/TestInputSocket.py
+++ b/tests/TestInputSocket.py
@@ -3,7 +3,7 @@
 # A test case that checks that providing commands to JSBSim via an input socket
 # is working.
 #
-# Copyright (c) 2015-2022 Bertrand Coconnier
+# Copyright (c) 2015-2024 Bertrand Coconnier
 #
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
@@ -19,74 +19,86 @@
 # this program; if not, see <http://www.gnu.org/licenses/>
 #
 
-import telnetlib, socket, time
+import asyncio
 import xml.etree.ElementTree as et
-from JSBSim_utils import JSBSimTestCase, CopyAircraftDef, RunTest
+
+import telnetlib3
+from JSBSim_utils import CopyAircraftDef, JSBSimTestCase, RunTest
 
 
 class TelnetInterface:
-    def __init__(self, fdm, port):
-        self.tn = telnetlib.Telnet("localhost", port)
-        self.fdm = fdm
-        fdm.run()
-
-    def __del__(self):
-        if (
-            "tn" in self.__dict__.keys()
-        ):  # Check if the Telnet session has been succesfully open
-            self.tn.close()
-        self.fdm = None
-
-    def sendCommand(self, command):
-        self.tn.write("{}\n".format(command).encode())
-        for _ in range(50):
-            self.fdm.run()
-            self.fdm.check_incremental_hold()
-        return self.getOutput()
-
-    def getOutput(self):
-        time.sleep(1.0) # Wait for the socket to process all the data.
-        return self.tn.read_very_eager().decode()
-
-    def getPropertyValue(self, property):
-        msg = self.sendCommand(f"get {property}").split("\n")
+    reader = None
+    writer = None
+
+    async def run(self, port, shell):
+        self.reader, self.writer = await telnetlib3.open_connection(
+            "localhost", port, shell=shell
+        )
+        await self.writer.protocol.waiter_closed
+
+    async def get_output(self):
+        msg = await self.reader.read(1024)
+        lines = msg.split("\r\n")
+        if lines[-1] == "JSBSim> ":
+            return "\n".join(lines[:-1])
+        else:
+            prompt = await self.reader.read(1024)
+            assert prompt == "JSBSim> "
+            return "\n".join(lines)
+
+    async def send_command(self, command):
+        self.writer.write(f"{command}\n")
+        await self.writer.drain()
+        return await self.get_output()
+
+    async def get_property_value(self, property_name):
+        msg = (await self.send_command(f"get {property_name}")).split("\n")
         return float(msg[0].split("=")[1])
 
 
 class TestInputSocket(JSBSimTestCase):
-    def setUp(self):
-        JSBSimTestCase.setUp(self)
+    def setUp(self, *args):
+        super().setUp(*args)
+        self._fdm = self.create_fdm()
+        self.telnet = TelnetInterface()
         self.script_path = self.sandbox.path_to_jsbsim_file("scripts", "c1722.xml")
-
-    def sanityCheck(self, tn):
-        # Check that the connection has been established
-        out = tn.getOutput()
-        self.assertTrue(
-            out.split("\n")[0] == "Connected to JSBSim server",
-            msg="Not connected to the JSBSim server.\nGot message '%s' instead"
-            % (out,),
-        )
-
-        # Check that "help" returns the minimum set of commands that will be
-        # tested
-        self.assertEqual(
-            sorted(
-                map(
-                    lambda x: x.split("{")[0].strip(),
-                    tn.sendCommand("help").split("\n")[2:-2],
-                )
-            ),
-            ["get", "help", "hold", "info", "iterate", "quit", "resume", "set"],
-        )
+        self.assertion_failed = False
+
+    def tearDown(self):
+        self.assertFalse(self.assertion_failed)
+        super().tearDown()
+        self._fdm = None
+        self.telnet = None
+
+    def assertEqual(self, first, second, msg=None):
+        try:
+            super().assertEqual(first, second, msg)
+        except AssertionError as e:
+            print(e, flush=True)
+            self.assertion_failed = True
+
+    def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None):
+        try:
+            super().assertAlmostEqual(first, second, places, msg, delta)
+        except AssertionError as e:
+            print(e, flush=True)
+            self.assertion_failed = True
+
+    async def run_fdm(self):
+        while True:
+            for _ in range(50):
+                if not self._fdm.run():
+                    return
+                self._fdm.check_incremental_hold()
+            await asyncio.sleep(0.1)
 
     def test_no_input(self):
-        fdm = self.create_fdm()
-        fdm.load_script(self.script_path)
-        fdm.run_ic()
-        fdm.hold()
+        self._fdm.load_script(self.script_path)
+        self._fdm.run_ic()
+        self._fdm.hold()
 
-        with self.assertRaises(socket.error):
-            TelnetInterface(fdm, 2222)
+        with self.assertRaises(OSError):
+            asyncio.run(self.run_test(2222, self.sanity_check))
 
     def test_input_socket(self):
         # First, extract the time step from the script file
@@ -95,93 +107,143 @@ def test_input_socket(self):
 
         # The aircraft c172x does not contain an <input> tag so we need
         # to add one.
-        tree, aircraft_name, b = CopyAircraftDef(self.script_path, self.sandbox)
+        tree, aircraft_name, _ = CopyAircraftDef(self.script_path, self.sandbox)
         root = tree.getroot()
         input_tag = et.SubElement(root, "input")
         input_tag.attrib["port"] = "1137"
         tree.write(self.sandbox("aircraft", aircraft_name, aircraft_name + ".xml"))
 
-        fdm = self.create_fdm()
-        fdm.set_aircraft_path("aircraft")
-        fdm.load_script(self.script_path)
-        fdm.run_ic()
-        fdm.hold()
+        self._fdm.set_aircraft_path("aircraft")
+        self._fdm.load_script(self.script_path)
+        self._fdm.run_ic()
+        self._fdm.hold()
+
+        asyncio.run(self.run_test(1137, lambda r, w: self.shell(dt, root, r, w)))
 
-        tn = TelnetInterface(fdm, 1137)
-        self.sanityCheck(tn)
+    async def shell(self, root, dt, reader, writer):
+        await self.sanity_check(reader, writer)
+        msg = (await self.telnet.send_command("info")).split("\n")
 
         # Check the aircraft name and its version
-        msg = tn.sendCommand("info").split("\n")
         self.assertEqual(msg[2].split(":")[1].strip(), root.attrib["name"].strip())
         self.assertEqual(msg[1].split(":")[1].strip(), root.attrib["version"].strip())
 
         # Check that the simulation time is 0.0
         self.assertEqual(float(msg[3].split(":")[1].strip()), 0.0)
-        self.assertEqual(fdm.get_sim_time(), 0.0)
-        self.assertEqual(tn.getPropertyValue("simulation/sim-time-sec"), 0.0)
+        self.assertEqual(self._fdm.get_sim_time(), 0.0)
+        self.assertEqual(
+            await self.telnet.get_property_value("simulation/sim-time-sec"),
+            0.0,
+        )
 
         # Check that 'iterate' iterates the correct number of times
-        tn.sendCommand("iterate 19")
-        self.assertEqual(fdm.get_sim_time(), 19.0 * dt)
+        await self.telnet.send_command("iterate 19")
+        self.assertEqual(self._fdm.get_sim_time(), 19.0 * dt)
         self.assertAlmostEqual(
-            tn.getPropertyValue("simulation/sim-time-sec"),
-            fdm.get_sim_time(),
+            await self.telnet.get_property_value("simulation/sim-time-sec"),
+            self._fdm.get_sim_time(),
             delta=1e-5,
         )
-        self.assertTrue(fdm.holding())
+        self.assertTrue(self._fdm.holding())
 
         # Wait a little bit and make sure that the simulation time has not
         # changed meanwhile thus confirming that the simulation is on hold.
-        for _ in range(40):
-            fdm.run()
-        self.assertEqual(fdm.get_sim_time(), 19.0 * dt)
+        await asyncio.sleep(0.5)
+        self.assertEqual(self._fdm.get_sim_time(), 19.0 * dt)
         self.assertAlmostEqual(
-            tn.getPropertyValue("simulation/sim-time-sec"),
-            fdm.get_sim_time(),
+            await self.telnet.get_property_value("simulation/sim-time-sec"),
+            self._fdm.get_sim_time(),
             delta=1e-5,
         )
 
         # Modify the tank[0] contents via the "send" command
-        half_contents = 0.5 * tn.getPropertyValue("propulsion/tank/contents-lbs")
-        tn.sendCommand("set propulsion/tank/contents-lbs " + str(half_contents))
+        half_contents = 0.5 * (
+            await self.telnet.get_property_value("propulsion/tank/contents-lbs")
+        )
+        await self.telnet.send_command(
+            "set propulsion/tank/contents-lbs " + str(half_contents)
+        )
         self.assertEqual(
-            tn.getPropertyValue("propulsion/tank/contents-lbs"), half_contents
+            await self.telnet.get_property_value("propulsion/tank/contents-lbs"),
+            half_contents,
         )
 
         # Check the resume/hold commands
-        t = fdm.get_sim_time()
-        tn.sendCommand("resume")
-        self.assertNotEqual(fdm.get_sim_time(), t)
-        self.assertFalse(fdm.holding())
-        tn.sendCommand("hold")
-        t = fdm.get_sim_time()
-        self.assertTrue(fdm.holding())
+        t = self._fdm.get_sim_time()
+        await self.telnet.send_command("resume")
+        self.assertNotEqual(self._fdm.get_sim_time(), t)
+        self.assertFalse(self._fdm.holding())
+        await self.telnet.send_command("hold")
+        t = self._fdm.get_sim_time()
+        self.assertTrue(self._fdm.holding())
         self.assertAlmostEqual(
-            tn.getPropertyValue("simulation/sim-time-sec"), t, delta=1e-5
+            await self.telnet.get_property_value("simulation/sim-time-sec"),
+            t,
+            delta=1e-5,
         )
 
         # Wait a little bit and make sure that the simulation time has not
         # changed meanwhile thus confirming that the simulation is on hold.
-        for _ in range(40):
-            fdm.run()
-        self.assertEqual(fdm.get_sim_time(), t)
+        await asyncio.sleep(0.5)
+        self.assertEqual(self._fdm.get_sim_time(), t)
         self.assertAlmostEqual(
-            tn.getPropertyValue("simulation/sim-time-sec"), t, delta=1e-5
+            await self.telnet.get_property_value("simulation/sim-time-sec"),
+            t,
+            delta=1e-5,
+        )
+
+        writer.close()
+
+    async def sanity_check(self, _, __):
+        out = await self.telnet.get_output()
+
+        self.assertTrue(
+            out == "Connected to JSBSim server",
+            msg=f"Not connected to the JSBSim server.\nGot message '{out}' instead",
         )
 
+        out = await self.telnet.send_command("help")
+
+        # Check that "help" returns the minimum set of commands that will be
+        # tested
+        self.assertEqual(
+            sorted(
+                map(
+                    lambda x: x.split("{")[0].strip(),
+                    out.split("\n")[2:-1],
+                )
+            ),
+            ["get", "help", "hold", "info", "iterate", "quit", "resume", "set"],
+        )
+
+    async def run_test(self, port, shell):
+        telnet_task = asyncio.create_task(self.telnet.run(port, shell))
+        fdm_task = asyncio.create_task(self.run_fdm())
+
+        done, pending = await asyncio.wait(
+            [telnet_task, fdm_task], return_when=asyncio.FIRST_COMPLETED
+        )
+
+        # Cancel the fdm_task if it is still pending
+        for task in pending:
+            task.cancel()
+
+        # Handle exceptions if any
+        for task in done:
+            if task.exception():
+                raise task.exception()
+
     def test_script_input(self):
         tree = et.parse(self.script_path)
         input_tag = et.SubElement(tree.getroot(), "input")
         input_tag.attrib["port"] = "1138"
         tree.write("c1722_1.xml")
 
-        fdm = self.create_fdm()
-        fdm.load_script("c1722_1.xml")
-        fdm.run_ic()
-        fdm.hold()
+        self._fdm.load_script("c1722_1.xml")
+        self._fdm.run_ic()
+        self._fdm.hold()
 
-        tn = TelnetInterface(fdm, 1138)
-        self.sanityCheck(tn)
+        asyncio.run(self.run_test(1138, self.sanity_check))
 
 
 RunTest(TestInputSocket)