前言
本文将和大家一起探讨python并发编程的实际项目:win图形界面应用(篇六,共八篇),系列文章将会从零开始构建项目,并逐渐完善项目,最终将项目打造成适用于高并发场景的应用。
本文为python并发编程的第二十四篇,上一篇文章地址如下:
python:并发编程(二十三)_Lion King的博客-CSDN博客
下一篇文章地址如下:
python:并发编程(二十五)_Lion King的博客-CSDN博客
一、协议界面设计
1、TCP Client界面
设计 TCP Client 界面可以根据需求和设计风格进行定制。以下是一种简单的设计示例,用于展示 TCP Client 的基本元素和布局:
(1)标签和输入框:用于输入服务器的 IP 地址和端口号。
(2)连接按钮:单击该按钮将与服务器建立连接。
(3)发送区域:包含文本框和发送按钮,用于输入要发送给服务器的消息和发送消息的操作。
(4)接收区域:用于显示从服务器接收到的消息。
下面是一个示例代码,展示了如何创建一个简单的 TCP Client 界面:
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import socket
import threading
class TCPClientFrame(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.create_widgets()
def create_widgets(self):
# IP 地址标签和输入框
ip_label = ttk.Label(self, text="服务器 IP 地址:")
ip_label.grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
self.ip_entry = ttk.Entry(self)
self.ip_entry.grid(row=0, column=1, padx=5, pady=5)
# 端口号标签和输入框
port_label = ttk.Label(self, text="服务器端口号:")
port_label.grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
self.port_entry = ttk.Entry(self)
self.port_entry.grid(row=1, column=1, padx=5, pady=5)
# 连接按钮
connect_button = ttk.Button(self, text="连接", command=self.connect_to_server)
connect_button.grid(row=2, column=0, columnspan=2, padx=5, pady=5)
# 发送区域
send_label = ttk.Label(self, text="发送消息:")
send_label.grid(row=3, column=0, padx=5, pady=5, sticky=tk.W)
self.send_entry = ttk.Entry(self)
self.send_entry.grid(row=3, column=1, padx=5, pady=5)
send_button = ttk.Button(self, text="发送", command=self.send_message)
send_button.grid(row=4, column=0, columnspan=2, padx=5, pady=5)
# 接收区域
receive_label = ttk.Label(self, text="接收消息:")
receive_label.grid(row=5, column=0, padx=5, pady=5, sticky=tk.W)
self.receive_text = tk.Text(self, height=5, width=30)
self.receive_text.grid(row=5, column=1, padx=5, pady=5)
# 初始化 TCP 连接对象和接收线程
self.tcp_client = None
self.receive_thread = None
def connect_to_server(self):
# 获取服务器 IP 地址和端口号
ip = self.ip_entry.get()
port = int(self.port_entry.get())
# 建立 TCP 连接
try:
self.tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_client.connect((ip, port))
messagebox.showinfo("连接成功", "成功连接到服务器")
# 启动接收线程
self.receive_thread = threading.Thread(target=self.receive_messages)
self.receive_thread.start()
except Exception as e:
messagebox.showerror("连接错误", str(e))
self.tcp_client = None
def send_message(self):
# 获取要发送的消息
message = self.send_entry.get()
# 发送消息给服务器
try:
self.tcp_client.send(message.encode())
self.receive_text.insert(tk.END, f"发送消息: {message}\n")
except Exception as e:
messagebox.showerror("发送错误", str(e))
# 清空发送输入框
self.send_entry.delete(0, tk.END)
def receive_messages(self):
while True:
try:
# 接收服务器消息
message = self.tcp_client.recv(1024).decode()
self.receive_text.insert(tk.END, f"接收消息: {message}\n")
except Exception as e:
# 出现错误时关闭连接并退出接收线程
self.tcp_client.close()
messagebox.showinfo("连接断开", "与服务器的连接已断开")
break
if __name__ == "__main__":
app = tk.Tk()
app.title("TCP Client")
tcp_client_frame = TCPClientFrame(app)
tcp_client_frame.pack()
app.mainloop()
每建立一个TCP连接,就会创建一个子线程。
2、将TCPclient界面嵌入到主界面中
import tkinter as tk
from tkinter import ttk
import socket
import threading
from tkinter import messagebox
class TCPClientFrame(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.create_widgets()
def create_widgets(self):
# IP 地址标签和输入框
ip_label = ttk.Label(self, text="服务器 IP 地址:")
ip_label.grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
self.ip_entry = ttk.Entry(self)
self.ip_entry.grid(row=0, column=1, padx=5, pady=5)
# 端口号标签和输入框
port_label = ttk.Label(self, text="服务器端口号:")
port_label.grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
self.port_entry = ttk.Entry(self)
self.port_entry.grid(row=1, column=1, padx=5, pady=5)
# 连接按钮
connect_button = ttk.Button(self, text="连接", command=self.connect_to_server)
connect_button.grid(row=2, column=0, columnspan=2, padx=5, pady=5)
# 发送区域
send_label = ttk.Label(self, text="发送消息:")
send_label.grid(row=3, column=0, padx=5, pady=5, sticky=tk.W)
self.send_entry = ttk.Entry(self)
self.send_entry.grid(row=3, column=1, padx=5, pady=5)
send_button = ttk.Button(self, text="发送", command=self.send_message)
send_button.grid(row=4, column=0, columnspan=2, padx=5, pady=5)
# 接收区域
receive_label = ttk.Label(self, text="接收消息:")
receive_label.grid(row=5, column=0, padx=5, pady=5, sticky=tk.W)
self.receive_text = tk.Text(self, height=5, width=30)
self.receive_text.grid(row=5, column=1, padx=5, pady=5)
# 初始化 TCP 连接对象和接收线程
self.tcp_client = None
self.receive_thread = None
def connect_to_server(self):
# 获取服务器 IP 地址和端口号
ip = self.ip_entry.get()
port = int(self.port_entry.get())
# 建立 TCP 连接
try:
self.tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_client.connect((ip, port))
messagebox.showinfo("连接成功", "成功连接到服务器")
# 启动接收线程
self.receive_thread = threading.Thread(target=self.receive_messages)
self.receive_thread.start()
except Exception as e:
messagebox.showerror("连接错误", str(e))
self.tcp_client = None
def send_message(self):
# 获取要发送的消息
message = self.send_entry.get()
# 发送消息给服务器
try:
self.tcp_client.send(message.encode())
self.receive_text.insert(tk.END, f"发送消息: {message}\n")
except Exception as e:
messagebox.showerror("发送错误", str(e))
# 清空发送输入框
self.send_entry.delete(0, tk.END)
def receive_messages(self):
while True:
try:
# 接收服务器消息
message = self.tcp_client.recv(1024).decode()
self.receive_text.insert(tk.END, f"接收消息: {message}\n")
except Exception as e:
# 出现错误时关闭连接并退出接收线程
self.tcp_client.close()
messagebox.showinfo("连接断开", "与服务器的连接已断开")
break
class ProtocolTestApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("协议测试")
self.geometry("400x300")
self.notebook = ttk.Notebook(self)
self.notebook.pack(fill="both", expand=True)
self.create_menu()
def create_menu(self):
menu_bar = tk.Menu(self)
# 创建客户端菜单和子菜单
client_menu = tk.Menu(menu_bar, tearoff=0)
client_menu.add_command(label="TCP Client", command=self.create_tcp_client_tab)
client_menu.add_command(label="UDP Client", command=self.create_udp_client_tab)
menu_bar.add_cascade(label="客户端", menu=client_menu)
# 创建服务端菜单和子菜单
server_menu = tk.Menu(menu_bar, tearoff=0)
server_menu.add_command(label="TCP Server", command=self.create_tcp_server_tab)
server_menu.add_command(label="UDP Server", command=self.create_udp_server_tab)
menu_bar.add_cascade(label="服务端", menu=server_menu)
# 将菜单栏配置到窗口
self.config(menu=menu_bar)
def create_tcp_client_tab(self):
# 创建TCP Client的tab页
tcp_client_frame = ttk.Frame(self.notebook)
tcp_client_frame.pack(fill="both", expand=True)
self.notebook.add(tcp_client_frame, text="TCP Client")
# 创建关闭按钮
close_button = ttk.Button(tcp_client_frame, text="×", width=8, command=lambda: self.close_tab(tcp_client_frame))
close_button.place(relx=1.0, rely=0.0, anchor="ne")
# 创建 TCP Client 的界面
tcp_client = TCPClientFrame(tcp_client_frame)
tcp_client.pack()
def create_udp_client_tab(self):
# 创建UDP Client的tab页
udp_client_frame = ttk.Frame(self.notebook)
udp_client_frame.pack(fill="both", expand=True)
self.notebook.add(udp_client_frame, text="UDP Client")
# 创建关闭按钮
close_button = ttk.Button(udp_client_frame, text="×", width=8, command=lambda: self.close_tab(udp_client_frame))
close_button.place(relx=1.0, rely=0.0, anchor="ne")
def create_tcp_server_tab(self):
# 创建TCP Server的tab页
tcp_server_frame = ttk.Frame(self.notebook)
tcp_server_frame.pack(fill="both", expand=True)
self.notebook.add(tcp_server_frame, text="TCP Server")
# 创建关闭按钮
close_button = ttk.Button(tcp_server_frame, text="×", width=8, command=lambda: self.close_tab(tcp_server_frame))
close_button.place(relx=1.0, rely=0.0, anchor="ne")
def create_udp_server_tab(self):
# 创建UDP Server的tab页
udp_server_frame = ttk.Frame(self.notebook)
udp_server_frame.pack(fill="both", expand=True)
self.notebook.add(udp_server_frame, text="UDP Server")
# 创建关闭按钮
close_button = ttk.Button(udp_server_frame, text="×", width=8, command=lambda: self.close_tab(udp_server_frame))
close_button.place(relx=1.0, rely=0.0, anchor="ne")
def close_tab(self, frame):
self.notebook.hide(frame)
if __name__ == "__main__":
app = ProtocolTestApp()
app.mainloop()
以上代码是在原有的 ProtocolTestApp
类的基础上进行了修改和扩展,主要实现了 TCP Client 界面的设计和功能。
在 ProtocolTestApp
类中,我们添加了一个新的方法 create_tcp_client_tab
,用于创建 TCP Client 的选项卡。在该选项卡中,我们创建了一个 TCPClientFrame
对象,并将其添加到选项卡的 tcp_client_frame
中。
TCPClientFrame
是一个自定义的 tkinter
Frame,用于显示 TCP Client 的界面和处理相关功能。在 TCPClientFrame
中,我们添加了一个 ttk.Entry
组件用于输入服务器的 IP 地址,一个 ttk.Button
组件用于连接服务器,一个 tkinter.Text
组件用于显示接收到的消息,以及一个 ttk.Entry
组件用于输入要发送的消息和一个 ttk.Button
组件用于发送消息。
在连接按钮的回调函数中,我们获取输入的 IP 地址并尝试与服务器建立连接。如果连接成功,我们创建一个新的线程来接收服务器发送的消息,并将其显示在接收文本框中。
发送按钮的回调函数中,我们获取输入的消息,并通过 TCP Client 的连接发送给服务器。
最后,通过修改 ProtocolTestApp
类的 create_tcp_client_tab
方法,将 TCPClientFrame
添加到 TCP Client 的选项卡中,从而实现了 TCP Client 界面的设计和功能。
整体而言,以上代码通过创建自定义的 TCPClientFrame
类和在选项卡中添加相应的组件,实现了 TCP Client 的界面设计和与服务器的连接、发送和接收消息的功能。