目录
前言
最近由于某些特殊项目的需要,匆忙通过 python 实现了一个简易版的 Telnet 客户端。这个客户端目前来说是有缺陷的(比如,不能 vi 读写文件内容,不能执行阻塞型命令 ping 等),但是感觉比较好玩,所以想与大家分享一下。
1. Telnet 客户端框架
2. Telnet 代码分解
接下来,我们就基于 Telnet客户端框架 来逐步拆分讲解代码构成。
2.1 基于 TK 创建会话窗口
# 创建窗口并设置标题
window = tk.Tk()
window.title("Telnet客户端")
# 设置tk.Tk窗口的背景颜色为浅蓝色
window.config(bg="sky blue")
2.1.1 创建 Text 文本控件
# 创建一个Text文本控件
console_text = tk.Text(window, width=200, height=70, bg="black", fg="white")
console_text.pack(pady=5)
# 定义一个字体样式
console_text.tag_configure("bold_tag", font=("Arial", 11))
2.1.2 创建 Frame 容器
# 创建一个Frame用于放置输入框和按钮,并设置Frame在下方空开5个空格
frame = tk.Frame(window, bg="sky blue")
frame.pack(pady=5)
2.1.2.1 基于 Frame 容器创建主机地址输入框
# 创建一个输入框控件,并放置在Frame中,同时设置右侧空开3个空格
host_addr_entry = tk.Entry(frame, width=50, font=("Arial", 12, "bold"))
host_addr_entry.insert(0, "请输入主机地址...")
host_addr_entry.config(fg="gray")
host_addr_entry.pack(side=tk.LEFT, padx=3)
2.1.2.1.1 主机地址输入框绑定焦点事件
# 绑定获取焦点和失去焦点事件,显示和隐藏提示语
host_addr_entry.bind("<FocusIn>", on_host_entry_focus_in)
host_addr_entry.bind("<FocusOut>", on_host_entry_focus_out)
# 获取焦点事件
def on_host_entry_focus_in(event):
if host_addr_entry.get() == "请输入主机地址...":
host_addr_entry.delete(0, tk.END)
host_addr_entry.config(fg="black")
# 失去焦点事件
def on_host_entry_focus_out(event):
if host_addr_entry.get() == "":
host_addr_entry.insert(0, "请输入主机地址...")
host_addr_entry.config(fg="gray")
2.1.2.2 创建 Telnet 连接按钮控件
# 创建一个按钮控件,并放置在Frame中
connect_button = tk.Button(frame, text="连接 Telnet", font=("Arial", 12, "bold"), fg="white", bg="#46C0FB", command=connect_telnet)
connect_button.pack(side=tk.LEFT, padx=3)
2.1.2.3 创建 Telnet 断开按钮控件
# 创建断开按钮控件,并放置在Frame中,右侧空开3个空格
disconnect_button = tk.Button(frame, text="断开 Telnet", font=("Arial", 12, "bold"), fg="white", bg="#46C0FB", command=disconnect_telnet, state=tk.DISABLED)
disconnect_button.pack(side=tk.LEFT, padx=3)
2.1.2.4 基于 Frame 容器创建发送命令输入框
# 创建一个输入框控件,并放置在Frame中,同时设置右侧空开3个空格
command_entry = tk.Entry(frame, width=50, font=("Arial", 12, "bold"))
command_entry.insert(0, "请发送指令...")
command_entry.config(fg="gray")
command_entry.pack(side=tk.LEFT, padx=12)
2.1.2.4.1 发送命令输入框绑定焦点事件
# 绑定获取焦点和失去焦点事件,显示和隐藏提示语
command_entry.bind("<FocusIn>", on_entry_focus_in)
command_entry.bind("<FocusOut>", on_entry_focus_out)
# 获取焦点事件
def on_entry_focus_in(event):
if command_entry.get() == "请发送指令...":
command_entry.delete(0, tk.END)
command_entry.config(fg="black")
# 失去焦点事件
def on_entry_focus_out(event):
if command_entry.get() == "":
command_entry.insert(0, "请发送指令...")
command_entry.config(fg="gray")
2.1.2.5 创建执行命令按钮控件
# 创建执行按钮控件,并放置在Frame中,右侧空开3个空格
execute_button = tk.Button(frame, text="执行命令", font=("Arial", 12, "bold"), fg="white", bg="#46C0FB", command=execute_command, state=tk.DISABLED)
execute_button.pack(side=tk.LEFT)
2.2 Telnet 连接与断开服务器过程
2.2.1 Telnet 连接服务器
def connect_telnet():
host = host_addr_entry.get() # 获取输入框内的主机地址
port = 23 # 默认telnet端口
username = 'root' # 登录用户名
password = '123456' # 登录密码
try:
# 连接Telnet服务器
telnet_client = telnetlib.Telnet(host, port, timeout=5)
console_text.insert(tk.END, "成功连接到Telnet服务器。\n", "bold_tag")
connect_button.config(state=tk.DISABLED)
disconnect_button.config(state=tk.NORMAL)
execute_button.config(state=tk.NORMAL)
# 输入登录用户名
telnet_client.read_until(b'login:')
telnet_client.write(username.encode('ascii') + b"\n")
# 输入登录密码
telnet_client.read_until(b'Password:')
telnet_client.write(password.encode('ascii') + b"\n")
print('成功登录到设备:' + host)
# 启动后台信息接收线程
receive_thread = threading.Thread(target=receive_background_info)
receive_thread.start()
except Exception as e:
console_text.insert(tk.END, f"连接失败: {e}\n", "bold_tag")
2.2.2 Telnet 断开服务器
def disconnect_telnet():
if telnet_client:
telnet_client.close()
console_text.insert(tk.END, "已断开Telnet连接。\n", "bold_tag")
connect_button.config(state=tk.NORMAL)
disconnect_button.config(state=tk.DISABLED)
execute_button.config(state=tk.DISABLED)
2.3 Telnet 会话交互过程
2.3.1 Telnet 客户端向服务器发送命令
def execute_command():
command = command_entry.get() + "\n"
telnet_client.write(command.encode('ascii'))
command_entry.delete(0, tk.END)
2.3.2 Telnet 客户端接收服务器返回的数据
def receive_background_info():
while True:
try:
output_data = telnet_client.read_very_eager().decode('utf-8')
if output_data:
clean_color_data = remove_color_codes(output_data.strip())
console_text.insert(tk.END, clean_color_data, "bold_tag")
time.sleep(0.5)
except EOFError:
break
except Exception as e:
console_text.insert(tk.END, f"错误: {e}\n", "bold_tag")
break
def remove_color_codes(data):
# 使用正则表达式匹配ANSI转义码,并替换为空字符串
ansi_escape = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
return ansi_escape.sub('', data)
3. Telnet 客户端代码总览
import tkinter as tk
import telnetlib
import threading
import time
import re
class TeneltTool:
def __init__(self):
self.window = tk.Tk()
self.window.title("Telnet客户端")
# 设置tk.Tk窗口的背景颜色为浅蓝色
self.window.config(bg="sky blue")
# 创建一个200x70的Text文本控件,并且设置背景为黑色
self.console_text = tk.Text(self.window, width=200, height=70, bg="black", fg="white")
self.console_text.pack(pady=5)
# 定义一个字体样式
self.console_text.tag_configure("bold_tag", font=("Arial", 11))
# 创建一个Frame用于放置输入框和按钮,并设置Frame在下方空开5个空格
frame = tk.Frame(self.window, bg="sky blue")
frame.pack(pady=5)
# 创建一个输入框控件,并放置在Frame中,同时设置右侧空开3个空格
self.host_addr_entry = tk.Entry(frame, width=50, font=("Arial", 12, "bold"))
self.host_addr_entry.insert(0, "请输入主机地址...")
self.host_addr_entry.config(fg="gray")
self.host_addr_entry.pack(side=tk.LEFT, padx=3)
# 创建一个按钮控件,并放置在Frame中
self.connect_button = tk.Button(frame, text="连接 Telnet", font=("Arial", 12, "bold"), fg="white", bg="#46C0FB",
command=self.connect_telnet)
self.connect_button.pack(side=tk.LEFT, padx=3)
# 创建断开按钮控件,并放置在Frame中,右侧空开3个空格
self.disconnect_button = tk.Button(frame, text="断开 Telnet", font=("Arial", 12, "bold"), fg="white",
bg="#46C0FB", command=self.disconnect_telnet, state=tk.DISABLED)
self.disconnect_button.pack(side=tk.LEFT, padx=3)
# 绑定获取焦点和失去焦点事件,显示和隐藏提示语
self.host_addr_entry.bind("<FocusIn>", self.on_host_entry_focus_in)
self.host_addr_entry.bind("<FocusOut>", self.on_host_entry_focus_out)
# 创建一个输入框控件,并放置在Frame中,同时设置右侧空开3个空格
self.command_entry = tk.Entry(frame, width=50, font=("Arial", 12, "bold"))
self.command_entry.insert(0, "请发送指令...")
self.command_entry.config(fg="gray")
self.command_entry.pack(side=tk.LEFT, padx=12)
# 绑定获取焦点和失去焦点事件,显示和隐藏提示语
self.command_entry.bind("<FocusIn>", self.on_entry_focus_in)
self.command_entry.bind("<FocusOut>", self.on_entry_focus_out)
# 创建执行按钮控件,并放置在Frame中,右侧空开3个空格
self.execute_button = tk.Button(frame, text="执行命令", font=("Arial", 12, "bold"), fg="white",
bg="#46C0FB", command=self.execute_command, state=tk.DISABLED)
self.execute_button.pack(side=tk.LEFT)
self.telnet_client = None
self.receive_thread = None
def on_host_entry_focus_in(self, event):
if self.host_addr_entry.get() == "请输入主机地址...":
self.host_addr_entry.delete(0, tk.END)
self.host_addr_entry.config(fg="black")
def on_host_entry_focus_out(self, event):
if self.host_addr_entry.get() == "":
self.host_addr_entry.insert(0, "请输入主机地址...")
self.host_addr_entry.config(fg="gray")
def on_entry_focus_in(self, event):
if self.command_entry.get() == "请发送指令...":
self.command_entry.delete(0, tk.END)
self.command_entry.config(fg="black")
def on_entry_focus_out(self, event):
if self.command_entry.get() == "":
self.command_entry.insert(0, "请发送指令...")
self.command_entry.config(fg="gray")
def connect_telnet(self):
host = self.host_addr_entry.get() # 获取输入框内的主机地址
port = 23 # 默认telnet端口
username = 'root' # 登录用户名
password = '123456' # 登录密码
try:
# 连接Telnet服务器
self.telnet_client = telnetlib.Telnet(host, port, timeout=5)
self.console_text.insert(tk.END, "成功连接到Telnet服务器。\n", "bold_tag")
self.connect_button.config(state=tk.DISABLED)
self.disconnect_button.config(state=tk.NORMAL)
self.execute_button.config(state=tk.NORMAL)
# 输入登录用户名
self.telnet_client.read_until(b'login:')
self.telnet_client.write(username.encode('ascii') + b"\n")
# 输入登录密码
self.telnet_client.read_until(b'Password:')
self.telnet_client.write(password.encode('ascii') + b"\n")
print('成功登录到设备:' + host)
# 启动后台信息接收线程
self.receive_thread = threading.Thread(target=self.receive_background_info)
self.receive_thread.start()
except Exception as e:
self.console_text.insert(tk.END, f"连接失败: {e}\n", "bold_tag")
def disconnect_telnet(self):
if self.telnet_client:
self.telnet_client.close()
self.console_text.insert(tk.END, "已断开Telnet连接。\n", "bold_tag")
self.connect_button.config(state=tk.NORMAL)
self.disconnect_button.config(state=tk.DISABLED)
self.execute_button.config(state=tk.DISABLED)
def execute_command(self):
command = self.command_entry.get() + "\n"
self.telnet_client.write(command.encode('ascii'))
self.command_entry.delete(0, tk.END)
def receive_background_info(self):
while True:
try:
output_data = self.telnet_client.read_very_eager().decode('utf-8')
if output_data:
clean_color_data = self.remove_color_codes(output_data.strip())
self.console_text.insert(tk.END, clean_color_data, "bold_tag")
# self.console_text.see(tk.END)
time.sleep(0.5)
except EOFError:
break
except Exception as e:
self.console_text.insert(tk.END, f"错误: {e}\n", "bold_tag")
break
def remove_color_codes(self, data):
# 使用正则表达式匹配ANSI转义码,并替换为空字符串
ansi_escape = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
return ansi_escape.sub('', data)
if __name__ == "__main__":
tenelt_tool = TeneltTool()
tenelt_tool.window.mainloop()
4. Telnet 客户端效果展示
5. 总结
由于这个工具是匆忙之间创作的,所以还存在一些缺陷,大家如果感兴趣,就拷贝源码自行优化吧。
好啦,本章内容到此结束,感谢大家的热爱与支持~