当前位置: 首页 > news >正文

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

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

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

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

一、效果图

1、udp server界面

在这里插入图片描述

2、第一个客户端

在这里插入图片描述

3、第二个客户端

在这里插入图片描述

二、server 项目代码

1、启动代码

# -*- coding: utf-8 -*-
import sys
from module.main import MainWindow
from PyQt6.QtWidgets import QApplicationif __name__ == '__main__':app = QApplication(sys.argv)mainWin = MainWindow()sys.exit(app.exec())

2、主控代码

"""
主窗口模块
"""
import socketfrom PyQt6 import QtWidgetsfrom server import UDPServer
from ui.ui_Main import Ui_MainWindowclass MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):"""主窗口初始化"""def __init__(self):super(MainWindow, self).__init__()self.udpServer = Noneself.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 socketfrom PyQt6.QtCore import QThread, pyqtSignalclass 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  # socketself.socketThread = None  # 新的 socket receive 线程self.clientList = []self.start()def start(self):if not self.is_running:self.is_running = Trueself.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 + ':' + messageself.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 = serverSocketself.is_running = Truedef 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 = Falsebreak

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, QtWidgetsclass 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.translateMainWindow.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 QApplicationif __name__ == '__main__':app = QApplication(sys.argv)main = MainWindow()sys.exit(app.exec())

2、主控代码

"""
主窗口模块
"""
import socketfrom PyQt6 import QtWidgetsfrom client import UDPClient
from ui.ui_Main import Ui_MainWindowclass MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):"""主窗口初始化"""def __init__(self):super(MainWindow, self).__init__()self.udpClient = Noneself.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, pyqtSignalclass UDPClient:def __init__(self, ui, ip, clientName, port):self.ui = uiself.ip = ipself.hostName = clientNameself.port = portself.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)self.socketThread = Noneself.startSocketReceiveThread()def send_udp_data(self, sentence):sentence = self.hostName + ":" + sentenceself.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 = clientSocketself.addr = Noneself.is_running = Truedef 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 = Falsebreak

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, QtWidgetsclass 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.translateMainWindow.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等。

相关文章:

  • docker的镜像创建 dockerfile
  • 【头歌-Python】Python第五章作业(初级)(7~16)
  • Mac安装DevEco Studio
  • 蓝牙配对、连接和删除汇总
  • AOP面向切面编程
  • 前端框架(Front-end Framework)和库(Library)的区别
  • Java接入ChatGPT接口简单示例
  • Android 11.0 systemui锁屏页面时钟显示样式的定制功能实现
  • 设计模式——建造者模式(创建型)
  • 【JavaFX】实现计算器小程序
  • PyCharm连接远程服务器上Docker容器,使用远程服务器的python intercepter解释器和GPU资源 [本地调试深度学习代码]
  • 解决Sortable拖动el-table表头时,由于选择列造成的拖拽顺序错乱的bug
  • 排序-选择排序与堆排序
  • ubuntu18.04配置cuda+cudnn+tensorrt+anconda+pytorch-gpu+pycharm
  • 开源治理典型案例分享(汇编转)
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【React系列】如何构建React应用程序
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • angular学习第一篇-----环境搭建
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • mysql外键的使用
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Python打包系统简单入门
  • Spring框架之我见(三)——IOC、AOP
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • 编写高质量JavaScript代码之并发
  • 翻译:Hystrix - How To Use
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 如何优雅地使用 Sublime Text
  • 深入浅出Node.js
  • 进程与线程(三)——进程/线程间通信
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ​如何防止网络攻击?
  • #define
  • #include<初见C语言之指针(5)>
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (7)STL算法之交换赋值
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (C++17) optional的使用
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (篇九)MySQL常用内置函数
  • (十六)串口UART
  • (数据结构)顺序表的定义
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .net MVC中使用angularJs刷新页面数据列表