python socket编程9 - PyQt6界面实现UDP server/client 多客户端通讯的例子

news/2024/5/18 14:26:10 标签: python, udp, socket

本篇实现 UDP server和client多客户端通讯的例子。

在UDP单机通讯的基础上进行重构,实现UDP server与多个 client通讯的例子。

创建两个 PyQt6的项目,一个作为UDP server 项目,另一个作为UDP client项目。

一、效果图

udp_server_6">1、udp server界面

在这里插入图片描述

2、第一个客户端

在这里插入图片描述

3、第二个客户端

在这里插入图片描述

二、server 项目代码

1、启动代码

# -*- coding: utf-8 -*-
import sys
from module.main import MainWindow
from PyQt6.QtWidgets import QApplication

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    sys.exit(app.exec())

2、主控代码

"""
主窗口模块
"""
import socket

from PyQt6 import QtWidgets

from server import UDPServer
from ui.ui_Main import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    """
        主窗口初始化
    """

    def __init__(self):
        super(MainWindow, self).__init__()
        self.udpServer = None
        self.setupUi(self)
        self.show()
        self.set_localhost_ip()
        self.set_localhost_port()
        self.pushButton_start.clicked.connect(self.udp_server_start)
        self.pushButton_send_message.clicked.connect(self.send_udp_data_to_client)

    def set_localhost_ip(self):
        host_ip = socket.gethostbyname_ex(socket.gethostname())
        self.lineEdit_IP.setText(host_ip[2][3])

    def set_localhost_port(self):
        self.lineEdit_port.setText('12000')
        self.lineEdit_name.setText('管理员')

    def udp_server_start(self):
        server_ip = self.lineEdit_IP.text()
        server_port = int(self.lineEdit_port.text())
        server_name = self.lineEdit_name.text()
        self.udpServer = UDPServer(self, server_ip, server_name, server_port)

    def send_udp_data_to_client(self):
        self.udpServer.send_data_to_client(self.lineEdit_message.text())

3、server完整代码

import socket

from PyQt6.QtCore import QThread, pyqtSignal


class UDPServer:
    def __init__(self, ui, server_ip, server_hostname, server_port):
        self.ui = ui  # 主界面
        self.ip = server_ip  # 服务器ip地址
        self.port = server_port  # 服务器端口号
        self.serverName = server_hostname  # 显示名称
        self.is_running = False  # 是否已经启动

        self.socket = None  # socket
        self.socketThread = None  # 新的 socket receive 线程
        self.clientList = []
        self.start()

    def start(self):
        if not self.is_running:
            self.is_running = True
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.socket.bind((self.ip, self.port))  # 绑定IP与端口
            # self.socket.listen(5)  # 设定最大连接数
            self.ui.statusbar.showMessage("UDP服务已经启动...")
            self.startSocketReceiveThread()

    def startSocketReceiveThread(self):
        self.socketThread = UDPServerSocketReceiveThread(self.socket)
        self.socketThread.receivedClientData.connect(self.show_client_message)
        self.socketThread.clientAddr.connect(self.server_status_trigger)
        self.socketThread.start()

    def send_data_to_client(self, message):
        message = self.serverName + ':' + message
        self.ui.textEdit.append(message)
        for cl in self.clientList:
            self.socket.sendto(message.encode(), cl)

    def server_status_trigger(self, clientAddr):
        if clientAddr not in self.clientList:
            self.clientList.append(clientAddr)
            self.ui.statusbar.showMessage("有新客户端接入:" + clientAddr[0] + ':' + str(clientAddr[1]))

    def show_client_message(self, message):
        self.ui.textEdit.append(message)


class UDPServerSocketReceiveThread(QThread):
    receivedClientData: pyqtSignal = pyqtSignal(str)  # 向主线程发送客户端的数据
    clientAddr: pyqtSignal = pyqtSignal(tuple)  # 向主线程发送服务器状态

    def __init__(self, serverSocket):
        super(UDPServerSocketReceiveThread, self).__init__()
        self.serverSocket = serverSocket
        self.is_running = True

    def run(self):
        self.startReceiveData()

    def startReceiveData(self):
        while self.is_running:
            try:
                message, clientAddress = self.serverSocket.recvfrom(2048)
                self.receivedClientData.emit(message.decode('utf-8'))
                self.clientAddr.emit(clientAddress)
            except ConnectionResetError as reason:
                self.is_running = False
                break

4、ui代码(PyQt6 Designer生成)

# Form implementation generated from reading ui file 'D:\projects\python-projects\multithread-udp-server\ui\Main.ui'
#
# Created by: PyQt6 UI code generator 6.4.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        font = QtGui.QFont()
        font.setPointSize(11)
        self.groupBox.setFont(font)
        self.groupBox.setObjectName("groupBox")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEdit_IP = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEdit_IP.setObjectName("lineEdit_IP")
        self.horizontalLayout.addWidget(self.lineEdit_IP)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout_2.addWidget(self.label_2)
        self.lineEdit_port = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEdit_port.setObjectName("lineEdit_port")
        self.horizontalLayout_2.addWidget(self.lineEdit_port)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_3.addWidget(self.label_3)
        self.lineEdit_name = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEdit_name.setObjectName("lineEdit_name")
        self.horizontalLayout_3.addWidget(self.lineEdit_name)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.verticalLayout_4.addWidget(self.groupBox)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
        self.horizontalLayout_4.addItem(spacerItem)
        self.pushButton_start = QtWidgets.QPushButton(parent=self.centralwidget)
        font = QtGui.QFont()
        font.setPointSize(11)
        self.pushButton_start.setFont(font)
        self.pushButton_start.setObjectName("pushButton_start")
        self.horizontalLayout_4.addWidget(self.pushButton_start)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
        self.horizontalLayout_4.addItem(spacerItem1)
        self.verticalLayout_3.addLayout(self.horizontalLayout_4)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.textEdit = QtWidgets.QTextEdit(parent=self.centralwidget)
        self.textEdit.setObjectName("textEdit")
        self.verticalLayout_2.addWidget(self.textEdit)
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.lineEdit_message = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEdit_message.setObjectName("lineEdit_message")
        self.horizontalLayout_5.addWidget(self.lineEdit_message)
        self.pushButton_send_message = QtWidgets.QPushButton(parent=self.centralwidget)
        font = QtGui.QFont()
        font.setPointSize(11)
        self.pushButton_send_message.setFont(font)
        self.pushButton_send_message.setObjectName("pushButton_send_message")
        self.horizontalLayout_5.addWidget(self.pushButton_send_message)
        self.verticalLayout_2.addLayout(self.horizontalLayout_5)
        self.verticalLayout_3.addLayout(self.verticalLayout_2)
        self.verticalLayout_4.addLayout(self.verticalLayout_3)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.groupBox.setTitle(_translate("MainWindow", "服务器配置:"))
        self.label.setText(_translate("MainWindow", "服务IP地址:"))
        self.label_2.setText(_translate("MainWindow", "服务端口:"))
        self.label_3.setText(_translate("MainWindow", "用户昵称:"))
        self.pushButton_start.setText(_translate("MainWindow", "启动服务"))
        self.pushButton_send_message.setText(_translate("MainWindow", "发送"))

三、客户端项目代码

1、启动代码

# -*- coding: utf-8 -*-
import sys
from module.main import MainWindow
from PyQt6.QtWidgets import QApplication

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindow()
    sys.exit(app.exec())

2、主控代码

"""
主窗口模块
"""
import socket

from PyQt6 import QtWidgets

from client import UDPClient
from ui.ui_Main import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    """
        主窗口初始化
    """

    def __init__(self):
        super(MainWindow, self).__init__()
        self.udpClient = None
        self.setupUi(self)
        self.show()
        self.set_localhost_ip()
        self.set_localhost_port()
        self.pushButton_send_message.clicked.connect(self.send_udp_data_to_server)

    def set_localhost_ip(self):
        host_ip = socket.gethostbyname_ex(socket.gethostname())
        self.lineEdit_IP.setText(host_ip[2][3])

    def set_localhost_port(self):
        self.lineEdit_port.setText('12000')
        self.lineEdit_name.setText('匿名用户')

    def send_udp_data_to_server(self):
        if self.udpClient is None:
            server_ip = self.lineEdit_IP.text()
            server_port = int(self.lineEdit_port.text())
            client_name = self.lineEdit_name.text()
            self.udpClient = UDPClient(self, server_ip, client_name, server_port)
        self.udpClient.send_udp_data(self.lineEdit_message.text())

3、client代码

import socket
from PyQt6.QtCore import QThread, pyqtSignal


class UDPClient:

    def __init__(self, ui, ip, clientName, port):
        self.ui = ui
        self.ip = ip
        self.hostName = clientName
        self.port = port

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socketThread = None
        self.startSocketReceiveThread()

    def send_udp_data(self, sentence):
        sentence = self.hostName + ":" + sentence
        self.ui.textEdit.append(sentence)
        self.socket.sendto(sentence.encode(), (self.ip, self.port))

    def startSocketReceiveThread(self):
        self.socketThread = UDPClientSocketReceiveThread(self.socket)
        self.socketThread.receivedServerData.connect(self.show_server_message)
        self.socketThread.start()

    def show_server_message(self, message):
        self.ui.textEdit.append(message)


class UDPClientSocketReceiveThread(QThread):
    receivedServerData: pyqtSignal = pyqtSignal(str)  # 向主线程发送接受到客户端的数据

    def __init__(self, clientSocket):
        super(UDPClientSocketReceiveThread, self).__init__()
        self.clientSocket = clientSocket
        self.addr = None
        self.is_running = True

    def run(self):
        self.startReceiveData()

    def startReceiveData(self):
        while self.is_running:
            try:
                message, clientAddress = self.clientSocket.recvfrom(2048)
                self.receivedServerData.emit(message.decode('utf-8'))
            except ConnectionResetError as reason:
                self.is_running = False
                break

4、ui代码(PyQt6 Designer生成)

# Form implementation generated from reading ui file 'D:\projects\python-projects\multithread-udp-client\ui\Main.ui'
#
# Created by: PyQt6 UI code generator 6.4.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        font = QtGui.QFont()
        font.setPointSize(11)
        self.groupBox.setFont(font)
        self.groupBox.setObjectName("groupBox")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEdit_IP = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEdit_IP.setObjectName("lineEdit_IP")
        self.horizontalLayout.addWidget(self.lineEdit_IP)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout_2.addWidget(self.label_2)
        self.lineEdit_port = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEdit_port.setObjectName("lineEdit_port")
        self.horizontalLayout_2.addWidget(self.lineEdit_port)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_3.addWidget(self.label_3)
        self.lineEdit_name = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEdit_name.setObjectName("lineEdit_name")
        self.horizontalLayout_3.addWidget(self.lineEdit_name)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.verticalLayout_3.addWidget(self.groupBox)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.textEdit = QtWidgets.QTextEdit(parent=self.centralwidget)
        self.textEdit.setObjectName("textEdit")
        self.verticalLayout_2.addWidget(self.textEdit)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.lineEdit_message = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEdit_message.setObjectName("lineEdit_message")
        self.horizontalLayout_4.addWidget(self.lineEdit_message)
        self.pushButton_send_message = QtWidgets.QPushButton(parent=self.centralwidget)
        font = QtGui.QFont()
        font.setPointSize(11)
        self.pushButton_send_message.setFont(font)
        self.pushButton_send_message.setObjectName("pushButton_send_message")
        self.horizontalLayout_4.addWidget(self.pushButton_send_message)
        self.verticalLayout_2.addLayout(self.horizontalLayout_4)
        self.verticalLayout_3.addLayout(self.verticalLayout_2)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.groupBox.setTitle(_translate("MainWindow", "服务器设置:"))
        self.label.setText(_translate("MainWindow", "服务器IP地址:"))
        self.label_2.setText(_translate("MainWindow", "服务端口:"))
        self.label_3.setText(_translate("MainWindow", "用户昵称:"))
        self.pushButton_send_message.setText(_translate("MainWindow", "发送"))

小结:

前面几篇文章,从命令行示例到UI界面示例,从单机通讯到多机通讯。
每个示例都是在上一个示例的基础上做了很小的一部分改动,易读易懂。

这些简单的示例搭建了一个测试环境,允许开发者在这个代码环境下对服务相关的参数进行修改,通过单个客户端或多个客户端连接,查看参数修改后的效果和影响。

也可以在这些示例的基础上,进行重构或二次封装成其他具体的应用,比如DHCP、web server、即时通讯IM server等。


http://www.niftyadmin.cn/n/5255897.html

相关文章

秒级监控、精准迅速:全面保障业务可用性 | 开源日报 No.101

louislam/uptime-kuma Stars: 41.1k License: MIT Uptime Kuma 是一个易于使用的自托管监控工具,主要功能和核心优势包括: 监控 HTTP(s) / TCP / HTTP(s) 关键词 / HTTP(s) Json 查询 / Ping / DNS 记录等服务的可用性提供时尚、响应迅速且良好用户体验…

FFmpeg-基础组件-AVFrame

本章主要介绍FFmpeg基础组件AVFrame. 文章目录 1.结构体成员2.成员函数AVFrame Host内存的获取 av_frame_get_bufferAVFrame device内存获取av_hwframe_get_buffer() 1.结构体成员 我们把所有的代码先粘贴上来,在后边一个一个解释。 typede…

基于Java教室管理系统

基于Java教室管理系统 功能需求 1、教室信息管理:系统需要提供教室信息管理功能,包括教室的基本信息(如教室号、教室类型、座位数等)和实时使用情况(如是否被预定、是否被占用等)。 2、课程信息管理&…

06-微服务架构之微服务设计指导书

文章目录 前言一、微服务常规的设计原则二、微服务的设计步骤1、确定服务边界,划分领域模型2、设计数据模型3、定义服务接口,确定通信机制4、服务的可用性和可伸缩性5、其他需要考虑的因素 总结 前言 经过前面的学习,我们对每个微服务的架构…

企业微信应用模板消息

是在发送应用消息接口的基础上,第三方应用支持一种新的消息类型:模板消息,msgtype指定为template_msg。模板消息是一种固定格式的消息。 注意 - 此消息类型目前仅第三方应用支持,自建应用不支持。服务商需在管理端申请模版。接口…

var、let、const 的区别?

var 1、var 声明的变量在全局内有效 2、可以重复声明 3、var 声明的变量存在变量提升 let 1、遇到{}可开启块级作用域 2、不能重复声明--- 可以防止变量重复定义产生的冲突,会直接报错 3、let 声明的变量不存在变量提升 const 1、const 声明…

抖音视频评论区采集软件使用教程

随着互联网的发展,短视频已经成为了人们生活中不可或缺的一部分。而抖音作为目前最受欢迎的短视频平台之一,其用户量已经达到了数亿之众。因此,很多商家和个人都希望能够从抖音中获得一些有用的信息,比如用户的兴趣爱好、消费习惯…

【lesson7】数据类型之string类型

文章目录 数据类型分类string类型set类型测试 enum类型测试 string类型的内容查找找所有女生(enum中)找爱好有游泳的人(set中)找到爱好中有足球和篮球的人 数据类型分类 string类型 set类型 说明: set:集…