Open In App

How to create telnet client with asyncio in Python

Last Updated : 27 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Telnet is a client/server application protocol that uses TCP/IP for connection. Telnet protocol enables a user to log onto and use a remote computer as though they were connected directly to it within the local network. The system that is being used by the user for the connection is the client and the remote computer being connected is the server. The commands entered on the terminal in a Telnet client are executed on the server (the remote computer) and the output of the command is directed to the client computer’s screen. Telnet is insecure as it uses plain text communication. The secure implementation of telnet that uses cryptography to encrypt data being transferred is SSH (Secure Shell). The telnet protocol is I/O bound, the I/O is very slow for e.g, the establishment of a connection with the server may take a long time due to slow speed on the server side, this results in a lot of idle CPU time since many such connections need to be created to the server, the asyncio library is the most suitable for such a task. 

Asyncio (Asynchronous Input/Output) is a python library for concurrent programming. Asynchronous is a style of programming in which two processes are being executed at the same time (not simultaneously). There is only one CPU core involved in computation (single thread), due to which the tasks take turns for execution and processing. Task 1 may start first and then mid-process the CPU starts execution of Task 2, the CPU may choose to switch between the tasks to optimize the usage of resources and minimize idle time.  

Steps to create a telnet client

1. Define an asynchronous telnet client function 

Using the asyncio.open_connection() coroutine with parameters host, port open a connection to the server, this function returns a reader and a writer that are instances of the class Streamreader and Streamwriter. These objects are used to read from the server and write to the server.

Write the username and password through the Streamwriter object to authenticate.

The statement awaits the reader.readline() returns a line that is received from the server, and yields the control while waiting for a response from the server, yielding control means informing the event loop that while the coroutine is waiting for a response go on and use the processing power for other computations. Print the response that is received from the server.

Once a connection is established successfully the writer object of the Streamwriter class can be used to send text commands that the server can implement locally and the results can be read via the object of the Streamreader class.

Close the connection.

2. Define an asynchronous main 

Within the main function call the telnet_client() coroutine with parameters hostname, a port that the server is listening on, username, and password. The await call yields the control while the telnet_client() coroutine is waiting for a connection. 

3. Asynchronous run the main function

The top-level entry point main() coroutine (since it is defined with async def) does not implement itself but rather needs to be run with the command. 

Python3




import asyncio
 
 
async def telnet_client(host: str, port: int, username: str, password: str) -> None:
    reader, writer = await asyncio.open_connection(host, port)
    print(f"connected to ({host}, {port})")
 
    # Login to the server, if the server requires authentication
    """ await writer.write(f"{username}\n".encode())
        await writer.write(f"{password}\n".encode()) """
 
    while True:
        command = input("\nEnter a command: ")
        if not command:
            print("[No command] ...closing connection ")
            break
        writer.write(f"{command}\n".encode())
        data = await reader.read(100000)
        print(data.decode().strip())
 
# there is a telnet server (telnet_server.py) listening on localhost port=23
asyncio.run(telnet_client("127.0.0.1", 2000, "username", "password"))


Telnet server

To test the client you must have a server to establish a connection to, following is a local server in python listening on port 2000. The setup is very similar to the client with the handle_client function working as the asynchronous listener. The run_command function spawns a process with command com, the stdin, stdout, stderr streams of the shell are captured and the data is returned to the client using the StreamWriter.

Python3




import asyncio
import subprocess
 
 
def run_command(com: str) -> None:
    try:
        pro = subprocess.run(com.split(), capture_output=True, text=True)
        if pro.stdout:
            return f"out----------------\n{pro.stdout}"
        elif pro.stderr:
            return f"err----------------\n {pro.stderr}"
        else:
            return f"[executed]"
    except Exception as ex:
        print("exception occurred", ex)
        return f"   [subprocess broke]"
 
 
async def handle_client(reader, writer):
    print(f"Connected to {writer.get_extra_info('peername')}")
 
    while True:
        data = await reader.read(100000)
        message = data.decode().strip()
        if not message:
            break
        print(f"Received message: {message}")
        res = run_command(message)
        writer.write(res.encode())
    print("Closing connection")
    writer.close()
 
 
async def start_server():
    server = await asyncio.start_server(handle_client, "127.0.0.1", 2000)
    print("Server started")
    await server.serve_forever()
 
asyncio.run(start_server())


Now run the following command in the terminal:

python telnet_server.py
python telnet_client.py

Output:

server started

client connected

commands executed



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads