猿创征文|服务端&客户端通信(基于Python)

发布于:2022-12-21 ⋅ 阅读:(558) ⋅ 点赞:(0)

《我的客户端开发之路》



前言

八月始一共发布更新了七篇内容,一方面是扩展知识,为有需要的小伙伴提供一些力所能及的帮助,另一方面也是为了服务于网络调试助手开发(简称网口吧),基本把开发网口的基础知识储备做完了。那么从这一篇开始就可以正式实战了——网络调试助手开发。整个项目分为三个部分,本片内容就从第一部分开始——服务端&客户端建立连接。建立连接这一块之前在一篇文章中已经详细提到过,所以本篇不再罗嗦,主要是分析代码,注释我也标上了,有地方不懂可以一起交流讨论。
与建立连接相关知识忘记的小伙伴可以参考链接:http://t.csdn.cn/saY6C

总结

这个过程并不难,ui页面的设计,转py文件,运行,一些语法等等之前发的文章内容也基本都涉及到了,也在本篇相关的使用位置给出了链接。中间又有几个小地方,关于获取文本控件和本机IP地址获取还有一些细节想说,但是想了想,为了不影响文章结构,这一篇就先这样吧,我再开个新的文章去补充这两处的细节,链接也会放在本篇内容的相应位置。代码不易,且行且珍惜。


一、服务端开发

  1. 先打开Qt-Designer,创建一个ui页面。
  2. 服务窗口信息下的控件是:QtextEdit。
  3. 其他的输入框控件是:QlineEdit。
  4. 发送和监听就是普通的按钮:QpushButton。

关于这两种文本框控件,细节之处参考链接:http://t.csdn.cn/cql3T
在这里插入图片描述

  1. 以下代码就是Qt-Designer自动生成的,它就是ui页面,前期建议自己写一写,掌握基础也很重要。如果小伙伴没有这个插件,可以直接复制下面的代码,效果是一样的。
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(1233, 700)
        self.textEdit = QtWidgets.QTextEdit(Dialog)
        self.textEdit.setGeometry(QtCore.QRect(140, 220, 571, 301))
        self.textEdit.setObjectName("textEdit")
        self.lineEdit2 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit2.setGeometry(QtCore.QRect(140, 90, 151, 31))
        self.lineEdit2.setObjectName("lineEdit2")
        self.lineEdit1 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit1.setGeometry(QtCore.QRect(140, 30, 301, 31))
        self.lineEdit1.setObjectName("lineEdit1")
        self.lineEdit3 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit3.setGeometry(QtCore.QRect(140, 560, 441, 31))
        self.lineEdit3.setObjectName("lineEdit3")
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(20, 170, 211, 51))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(50, 100, 91, 21))
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(Dialog)
        self.label_3.setGeometry(QtCore.QRect(50, 40, 81, 31))
        self.label_3.setObjectName("label_3")
        self.pushButton1 = QtWidgets.QPushButton(Dialog)
        self.pushButton1.setGeometry(QtCore.QRect(350, 90, 93, 28))
        self.pushButton1.setObjectName("pushButton1")
        self.pushButton2 = QtWidgets.QPushButton(Dialog)
        self.pushButton2.setGeometry(QtCore.QRect(600, 560, 91, 31))
        self.pushButton2.setObjectName("pushButton2")
        self.label_4 = QtWidgets.QLabel(Dialog)
        self.label_4.setGeometry(QtCore.QRect(60, 560, 72, 41))
        self.label_4.setObjectName("label_4")
        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "服务机"))
        self.label.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">服务窗口信息:</span></p></body></html>"))
        self.label_2.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">端口号:</span></p></body></html>"))
        self.label_3.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">IP地址:</span></p></body></html>"))
        self.pushButton1.setText(_translate("Dialog", "开启监听"))
        self.pushButton2.setText(_translate("Dialog", "发送"))
        self.label_4.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">输入:</span></p></body></html>"))


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)  # 创建应用
    MainWindow = QtWidgets.QMainWindow()  # 创建窗口
    ui = Ui_Dialog()  # 调用类
    ui.setupUi(MainWindow)  # 窗口数据初始化
    MainWindow.show()
    sys.exit(app.exec())
  1. 运行上面的代码,即ui页面,显示效果如下。
    注意:这只是个空壳,只能观赏,没有任何功能。
    在这里插入图片描述
  2. 页面已经有了,现在来编写它的功能。不要忽略:新建py文件,把写ui页面的py文件导入进来(把类复制粘贴也行,但是导包不是可以让代码更简洁,更方便嘛),在它的基础上继续完成添加功能。
    在获取主机IP时,有好几种方式,详细介绍参考链接:http://t.csdn.cn/cql3T
import sys
import socket
from PyQt5.QtWidgets import QApplication, QDialog
from threading import Thread
from severend import *  #这是我ui文件的命名,这里导入了ui页面
conn = None

# 添加背景
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

# 添加对文件操作的库
import openpyxl

# 绘制曲线图
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import make_interp_spline as spline


class Window(QDialog):  # QDialog标准对话框类
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()  # 对象的实例化,可调用类Ui_Dialog中的方法
        # 添加背景
        self.lab1 = QLabel(self)
        self.lab1.setPixmap(QPixmap("C:/Users/WZM/Pictures/Saved Pictures/509.jpg"))
        layout = QVBoxLayout()
        layout.addWidget(self.lab1)
        self.setLayout(layout)

        self.ui.setupUi(self)  # 初始化窗口,自动匹配符合onobjectName, signalName()命名管理的信号与槽
        self.textEdit = self.ui.textEdit  # 服务器发送的消息
        self.ui.pushButton2.clicked.connect(self.dispMessage)  # 连接槽函数
        self.show()

    def dispMessage(self):
        text = self.ui.lineEdit3.text()  # 获取输入文本
        global conn
        conn.send(text.encode("utf-8"))  # 编码格式后发送
        self.ui.textEdit.append("服务器: " + self.ui.lineEdit3.text())
        self.ui.lineEdit3.setText("")

class ServerThread(Thread):
    def __init__(self, Window, ip, port):
        Thread.__init__(self)
        self.window = Window
        self.ip = ip
        self.port = port


    def run(self):  # 重写方法
        BUFFER_SIZE = 1024  # 被加入缓冲器的元素的最大数
        tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)  # 建立socket套接字
        tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 操作套接字层,在绑定前调用让套接字允许地址重用,进程结束后立即复用端口
        tcpServer.bind((self.ip, self.port))  # 绑定地址端口
        threads = []  # 添加到并行集合线程
        tcpServer.listen(4)  # 监听连接,设置最大连接个数
        while True:
            global conn  # 全局变量监控多线程是否全部完成
            (conn, (ip, port)) = tcpServer.accept()  # 接受引入请求
            newthread = ClientThread(window, ip, port)  # 客户端线程引入
            newthread.start()  # 启动线程的活动
            threads.append(newthread)  # 添加到并行集合线程,这里只用到一个,也可以写多个
        for t in threads:   # 遍历线程
            t.join()  # 调用别的线程
        tcpServer.close()  # 关闭套接


class ClientThread(Thread):
    def __init__(self, Window, ip, port):
        Thread.__init__(self)
        self.window = Window
        self.ip = ip
        self.port = port

    def run(self):
        while True:
            global conn
            data = conn.recv(1024)  # TCP类型的数据接收
            window.textEdit.append("客户: " + data.decode("utf-8"))  # 在数组后加上相应元素

if __name__ == "__main__":
    app = QApplication(sys.argv)
    host = socket.gethostname()  # 获取主机名
    TCP_IP = socket.gethostbyname(host)
    TCP_PORT = 65003
    window = Window()
    serverThread = ServerThread(window, TCP_IP, TCP_PORT)
    serverThread.start()
    window.exec()
    sys.exit(app.exec())

  1. 再次运行,这个页面就不再是空壳了,它已经可以完成通信,互发消息了。效果如下:
    注意一点,互发消息需要客户端也准备好,运行后出现下面的效果肯定是没问题的,但是消息是发不出去的,因为客户端还没有编写呢,怎么能通信呢。
    在这里插入图片描述

二、客户端开发

  1. 上面已经做好了服务端,现在仍然是和服务端相同的流程创建客户端程序。
    在这里插入图片描述
  2. 我把ui页面的代码也放在这里,以防有的小伙伴没有工具。
    我就不再演示效果了吧,和服务端是一样的,你也可以自己运行一下看看效果。
    仍然是注意一点,这里也只是完成了客户端页面创建,也是个空壳子,没有功能操作的。
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("客户机")
        Dialog.resize(1233, 700)
        self.textEdit = QtWidgets.QTextEdit(Dialog)
        self.textEdit.setGeometry(QtCore.QRect(140, 220, 571, 301))
        self.textEdit.setObjectName("textEdit")
        self.lineEdit2 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit2.setGeometry(QtCore.QRect(140, 90, 151, 31))
        self.lineEdit2.setObjectName("lineEdit2")
        self.lineEdit1 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit1.setGeometry(QtCore.QRect(140, 30, 301, 31))
        self.lineEdit1.setObjectName("lineEdit1")
        self.lineEdit3 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit3.setGeometry(QtCore.QRect(140, 560, 441, 31))
        self.lineEdit3.setObjectName("lineEdit3")
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(20, 170, 211, 51))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(50, 100, 91, 21))
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(Dialog)
        self.label_3.setGeometry(QtCore.QRect(50, 40, 81, 31))
        self.label_3.setObjectName("label_3")
        self.pushButton1 = QtWidgets.QPushButton(Dialog)
        self.pushButton1.setGeometry(QtCore.QRect(350, 90, 93, 28))
        self.pushButton1.setObjectName("pushButton1")
        self.pushButton2 = QtWidgets.QPushButton(Dialog)
        self.pushButton2.setGeometry(QtCore.QRect(600, 560, 91, 31))
        self.pushButton2.setObjectName("pushButton2")
        self.label_4 = QtWidgets.QLabel(Dialog)
        self.label_4.setGeometry(QtCore.QRect(60, 560, 72, 41))
        self.label_4.setObjectName("label_4")
        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "客户机"))
        self.label.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">客户窗口信息:</span></p></body></html>"))
        self.label_2.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">端口号:</span></p></body></html>"))
        self.label_3.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">IP地址:</span></p></body></html>"))
        self.pushButton1.setText(_translate("Dialog", "连接"))
        self.pushButton2.setText(_translate("Dialog", "发送"))
        self.label_4.setText(_translate("Dialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:11pt; font-weight:600;\">输入:</span></p></body></html>"))


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)  # 创建应用
    MainWindow = QtWidgets.QMainWindow()  # 创建窗口
    ui = Ui_Dialog()  # 调用类
    ui.setupUi(MainWindow)  # 窗口数据初始化
    MainWindow.show()
    sys.exit(app.exec())
  1. 现在为ui页面添上功能操作。
    注意:新建py文件,把写客户端ui页面的py文件导入进来,然后在它基础上添加功能。
import sys
from PyQt5.QtWidgets import QApplication, QDialog
import socket
from threading import Thread
from clientend import *
tcpClientA = None

# 添加背景
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class Window(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()

        # 添加背景
        self.lab1 = QLabel(self)
        self.lab1.setPixmap(QPixmap("C:/Users/WZM/Pictures/Saved Pictures/509.jpg"))
        layout = QVBoxLayout()
        layout.addWidget(self.lab1)
        self.setLayout(layout)

        self.ui.setupUi(self)
        self.textEditMessages = self.ui.textEdit
        self.ui.pushButton2.clicked.connect(self.dispMessage)
        self.show()

    def dispMessage(self):
        text = self.ui.lineEdit3.text()
        self.ui.textEdit.append("客户: " + self.ui.lineEdit3.text())
        tcpClientA.send(text.encode("utf-8"))  # 编码格式后发送
        self.ui.lineEdit3.setText("")


class ClientThread(Thread):
    def __init__(self, Window, TCP_IP, TCP_PORT):
        Thread.__init__(self)
        self.window = Window
        self.TCP_IP = TCP_IP
        self.TCP_PORT = TCP_PORT

    def run(self):
        BUFFER_SIZE = 1024
        global tcpClientA
        tcpClientA = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)  # 建立socket套接字
        tcpClientA.connect((self.TCP_IP, self.TCP_PORT))  # 发出连接请求,建立与tcp服务器的连接
        while True:
            data = tcpClientA.recv(BUFFER_SIZE)  # 获取引入信息
            window.textEditMessages.append("服务器: " + data.decode("utf-8"))  # 解码后引入进程

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    host = socket.gethostname()  # 获取主机名
    ip = socket.gethostbyname(host)
    port = 65003
    clientThread = ClientThread(window, ip, port)
    clientThread.start()
    window.exec()
    sys.exit(app.exec())
    
  1. 现在再去运行服务端和客户端程序,就可以互相通信了。效果如下:
    显示发送或接收的信息,显示发送者是谁。
    在这里插入图片描述
本文含有隐藏内容,请 开通VIP 后查看