《我的客户端开发之路》
文章目录
前言
八月始一共发布更新了七篇内容,一方面是扩展知识,为有需要的小伙伴提供一些力所能及的帮助,另一方面也是为了服务于网络调试助手开发(简称网口吧),基本把开发网口的基础知识储备做完了。那么从这一篇开始就可以正式实战了——网络调试助手开发。整个项目分为三个部分,本片内容就从第一部分开始——服务端&客户端建立连接。建立连接这一块之前在一篇文章中已经详细提到过,所以本篇不再罗嗦,主要是分析代码,注释我也标上了,有地方不懂可以一起交流讨论。
与建立连接相关知识忘记的小伙伴可以参考链接:http://t.csdn.cn/saY6C
总结
这个过程并不难,ui页面的设计,转py文件,运行,一些语法等等之前发的文章内容也基本都涉及到了,也在本篇相关的使用位置给出了链接。中间又有几个小地方,关于获取文本控件和本机IP地址获取还有一些细节想说,但是想了想,为了不影响文章结构,这一篇就先这样吧,我再开个新的文章去补充这两处的细节,链接也会放在本篇内容的相应位置。代码不易,且行且珍惜。
一、服务端开发
- 先打开Qt-Designer,创建一个ui页面。
- 服务窗口信息下的控件是:QtextEdit。
- 其他的输入框控件是:QlineEdit。
- 发送和监听就是普通的按钮:QpushButton。
关于这两种文本框控件,细节之处参考链接:http://t.csdn.cn/cql3T
- 以下代码就是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())
- 运行上面的代码,即ui页面,显示效果如下。
注意:这只是个空壳,只能观赏,没有任何功能。
- 页面已经有了,现在来编写它的功能。不要忽略:新建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())
- 再次运行,这个页面就不再是空壳了,它已经可以完成通信,互发消息了。效果如下:
注意一点,互发消息需要客户端也准备好,运行后出现下面的效果肯定是没问题的,但是消息是发不出去的,因为客户端还没有编写呢,怎么能通信呢。
二、客户端开发
- 上面已经做好了服务端,现在仍然是和服务端相同的流程创建客户端程序。
- 我把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())
- 现在为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())
- 现在再去运行服务端和客户端程序,就可以互相通信了。效果如下:
显示发送或接收的信息,显示发送者是谁。
本文含有隐藏内容,请 开通VIP 后查看