目录
前言
1. Telnet 客户端框架
2. Telnet 代码分解
2.1 基于 TK 创建会话窗口
2.1.1 创建 Text 文本控件
2.1.2 创建 Frame 容器
2.1.2.1 基于 Frame 容器创建主机地址输入框
2.1.2.1.1 主机地址输入框绑定焦点事件
2.1.2.2 创建 Telnet 连接按钮控件
2.1.2.3 创建 Telnet 断开按钮控件
2.1.2.4 基于 Frame 容器创建发送命令输入框
2.1.2.4.1 发送命令输入框绑定焦点事件
2.1.2.5 创建执行命令按钮控件
2.2 Telnet 连接与断开服务器过程
2.2.1 Telnet 连接服务器
2.2.2 Telnet 断开服务器
2.3 Telnet 会话交互过程
2.3.1 Telnet 客户端向服务器发送命令
2.3.2 Telnet 客户端接收服务器返回的数据
3. Telnet 客户端代码总览
4. Telnet 客户端效果展示
5. 总结
前言
最近由于某些特殊项目的需要,匆忙通过 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:breakexcept Exception as e:console_text.insert(tk.END, f"错误: {e}\n", "bold_tag")breakdef 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 reclass 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 = Noneself.receive_thread = Nonedef 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:breakexcept Exception as e:self.console_text.insert(tk.END, f"错误: {e}\n", "bold_tag")breakdef 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. 总结
由于这个工具是匆忙之间创作的,所以还存在一些缺陷,大家如果感兴趣,就拷贝源码自行优化吧。
好啦,本章内容到此结束,感谢大家的热爱与支持~