python:并发编程(二十四)

前言

本文将和大家一起探讨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 的界面设计和与服务器的连接、发送和接收消息的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/31603.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NCI Architecture

2.1 组成部分 NCI 可分为以下逻辑组件:  NCI 核心 NCI 核心定义了设备主机 (DH) 和 NFC 控制器 (NFCC) 之间通信的基本功能。 这使得 NFCC 和 DH 之间能够进行控制消息(命令、响应和通知)和数据消息交换。  传输映射 传输映射定义 N…

【C++】哈希unordered系列容器的模拟实现

文章目录 一、哈希表的模拟实现(开散列)1. 开散列的概念2. 开散列的节点结构3. 开散列的插入删除与查找4. 开散列整体代码实现 二、unordered系列容器的封装实现(开散列)1. 迭代器2. unordered_set和unordered_map的封装实现3. 哈希表整体源码 一、哈希表…

如何让ChatGPT制作XMind思维导图

一、使用ChatGPT辅助生成内容 给大家一个思路,比如我想制作《股神巴菲特给儿女的一生忠告》相关的思维导图,那我们可以在ChatGPT上提问“请使用markdown格式写出股神巴菲特给儿女的一生忠告的思维导图,以代码格式输出”。 生成后&#xff0…

【小沐学Python】Python实现Web服务器(Flask,gevent )

文章目录 1、简介1.1 功能列表1.2 支持平台1.3 安装 2、gevent入门示例2.1 文件IO2.2 MySQL2.3 redis2.4 time2.5 requests2.6 socket2.7 并发抓取文字2.8 并发抓取图片2.9 生产者 - 消费者 3、gevent其他示例3.1 StreamServer3.2 WSGI server3.3 flask3.4 websocket3.5 udp 结…

压测工具Jmeter学习

压测工具Jmeter Jmeter介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。 它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI …

三层交换机与路由互联配置(华为设备)

#三层交换机与路由器配置配置 #三层交换机与路由器配置配置 路由器配置 #进入系统视图 <Huawei>system-view #关闭系统提示信息 [Huawei]undo info-center enable #配置一个环回口 [Huawei]int LoopBack 0 #配置IP地址 与 掩码 [Huawei-LoopBack0]ip address 1.1.…

源码编译LAMP与论坛安装

目录 前言 LAMP工作过程 Apache 1&#xff09;Apache主要特点 如何创建论坛 第一步 关闭防火墙和安全机制 第二、安装相关的Apache服务 1&#xff09;解压压缩包 2&#xff09;安装依赖环境 3&#xff09;配置安装路经等 4&#xff09;编译并安装 5&#xff09;优化…

C++【STL】之反向迭代器

反向迭代器 前面在vector和list的模拟实现中都有讲到正向迭代器&#xff0c;今天我们就来讲解一下反向迭代器的思想和模拟实现&#xff0c;在某些场景下还是很实用的&#xff0c;下面正文直接开始。 文章目录&#xff1a; 反向迭代器1. 反向迭代器结构2. 反向迭代器实现2.1 多…

基于Springboot+vue的汽车租赁系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

5.4.2 网络地址转换NAT

5.4.2 网络地址转换NAT 我们知道为了缓解IPv4地址紧缺的问题&#xff0c;相继出现了一系列缓解地址耗尽的解决方案&#xff0c;比如通过子网划分&#xff08;5.2.8 子网编址&#xff09;实现网络地址在多个物理网络之间的复用&#xff0c;通过无分类编址&#xff08;5.2.9 无分…

Android PagerSnapHelper改造RecyclerView为ViewPage,kotlin

Android PagerSnapHelper改造RecyclerView为ViewPage&#xff0c;kotlin <?xml version"1.0" encoding"utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tool…

端午节粽子(python)

目录 前言 正文 粽叶绘制 粽叶绳绘制 祝福语绘制 源代码 总结 前言 今天端午节&#xff0c;然后昨天也学习一下绘图的相关知识&#xff0c;然后就想看一下能不能画一下&#xff0c;结果还是有点困难的&#xff0c;用CharAI生成简直一言难尽了。后面是找了一个改了一下。 …

【剑指offer刷题记录 java版】数组双指针 之 其它题目

本系列文章记录labuladong的算法小抄中剑指offer题目 【剑指offer刷题记录 java版】数组双指针 之 其它题目 剑指 Offer II 018. 有效的回⽂剑指 Offer 58 - I. 翻转单词顺序剑指 Offer 21. 调整数组顺序使奇数位于偶数前⾯剑指 Offer 57. 和为s的两个数字剑指 Offer II 007. 数…

STM32单片机LED显示屏驱动原理与实现

STM32单片机驱动LED显示屏的原理与实现方法与Arduino类似&#xff0c;但涉及到的具体硬件资源和库函数可能会有所不同。下面是一个详细的介绍&#xff1a; 原理&#xff1a; STM32单片机驱动LED显示屏的原理是通过控制GPIO引脚的电平状态来控制LED的亮灭。通过设置引脚的输出电…

Mybatis源码分析_Mapper接口是如何实例化的 (2)

我们在使用Springmybatis的时候&#xff0c;经常都是直接写一个接口和一个对应的 ***Mapper.xml文件&#xff0c;然后业务代码就可以直接注入这个接口了。它是如何做到的呢&#xff1f; 接口&#xff1a; xml 想搞清楚这个问题&#xff0c;那还是要从Mybatis底层源码进行分析的…

轻量级的深度学习框架Tinygrad

Tinygrad是一个轻量级的深度学习库&#xff0c;它提供了一种简化和直观的方法来理解和实现神经网络。在本文中&#xff0c;我们将探讨Tinygrad及其主要功能&#xff0c;以及它如何成为那些开始深度学习之旅的人的有价值的工具。 什么是Tinygrad? Tinygrad是一个开源的深度学习…

Redis 通用命令

通用命令介绍 Redis 通用命令是一些 Redis 下可以作用在常用数据结构上的常用命令和一些基础的命令&#xff0c;比如删除键、对键进行改名、判断键是否存在等。简单说&#xff0c;就是 keys 分类的命令&#xff0c;如下图。 上图中圈中的部分&#xff0c;就是所谓的通用的命令…

【C语言初阶】带你轻松玩转所有常用操作符(1)

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,这里是君兮_&#xff0c;最近要准备期末复习了&#xff0c;可能更新的就不会那么频繁了&#xff0c;下个星期回复正常更新。 操作符详解1 前言一.操作符的分类二.算数操作符三.移位操作符1.二进制表示的三种形式2.…

matlab实现语音信号的频域分析及应用

1.语音信号本质上是非平稳信号。但我们可以假设语音信号在一个短时间内是平稳的&#xff0c;这样我们用稳态分析方法处理非平稳信号。应用在傅立叶分析就是短时傅立叶变换。 语音的频域分析&#xff1a;包括语音信号的频谱、功率谱、倒频谱、频谱包络等. 常用频域分析方法&am…

mySql和VSC++

确认主机服务里的mysql服务已打开 使用组合键“winR”运行“services.msc”&#xff0c;进入本地服务窗口&#xff1b; 2.进入本地服务窗口后&#xff0c;在右侧服务列表中&#xff0c;查找到“ mysql ”服务选项&#xff1b; 3.查找到mysql服务选项后&#xff0c;双击打开mysq…