PYQT5+pycharm将C++写的TCP服务器和客户端通信复刻实现
摘要:
用pyqt5+pycharm开发环境实现c++在QT环境下的程序代码,实现局域网TCP文件传输工具并带界面。
参考C++代码文献地址见文章底。
目录
-
程序功能
-
代码设计
1.服务端
2.客户端
1.程序功能
详细见源码:https://download.youkuaiyun.com/download/qq_42307630/21074738
1.在同一局域网内的两个设备,基于tcp网络编程,实现可靠的、高速的文件传输,并且实时显示传输进度和速度;采用客户端、服务端形式,满足双向传输;具有可扩展性、可移植性。实测传输速度可达到9Mb/s。
服务端
监听端口:当计算机网络底层收到tcp信息时,通过端口传递给相应的程序进行处理,也就是说一个端口只能被一个应用程序使用,但一个应用程序可以使用多个端口。
选择监听端口(为了避免已被其他程序使用,可设大一点),点击打开服务器,可更改接收文件的保存路径,等到客户端连接,客户端连接成功后,可用鼠标拖动文件至中间空白处,即可将文件传输到客户端。
客户端
输入服务端的ip地址(下面的是我的ip,请根据服务端本地ip修改)和监听的端口号,服务端的ip地址查看:右击电脑右下角网络图标->打开网络和共享中心->本地连接->详细信息->IP4地址,正确填写后点击连接服务器,服务器端将显示客户端已连接,接下来就可以开始文件传输了
2.代码设计
服务端
#!/usr/bin/env python
# coding=utf-8
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QMainWindow
from PyQt5.QtCore import QFile, QFileInfo, QTime, QTimer, QByteArray, pyqtSlot, QDir, QIODevice, QDataStream
from PyQt5.QtNetwork import QTcpSocket
from PyQt5.QtNetwork import QTcpServer, QHostAddress
from PyQt5.QtNetwork import QNetworkInterface
from Ui_mainwindow import Ui_MainWindow
import array
class eSTATUS:
ST_IDLE = 0
ST_SENDING = 1
ST_RECING = 2
class MyServer(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.tcpServer = QTcpServer()
self.tcpServer.newConnection.connect(self.do_connect)
# self.socket =QTcpSocket()
self.clientConnection = QTcpSocket()
self.localFile = QFile()
self.localFile = None
#self.loadSize = 64*1024 # 缓冲区大小
self.totalBytes = 0
self.bytesWriten = 0
self.bytesTowrite = 0
self.outblock = QByteArray()
self.numBytes = 0
self.filename = None
self.fileSavePath = 'C:/'
self.bytesReceived = 0
self.status = eSTATUS.ST_IDLE
self.temp = '保存路径:'+ self.fileSavePath
self.ui.label_5.setText(self.temp)
# 设置进度条
self.ui.progressBar.setValue(0)
self.ui.progressBar.setRange(0, 100)
self.ui.textEdit.setEnabled(True)
self.ui.lineEdit.setDisabled(True)
self.counter = QTime()
self.timer = QTimer()
self.timer.stop()
self.timer.timeout.connect(self.second_task)
@pyqtSlot()
def on_textEdit_textChanged(self): # 测试ok!!
filepath = self.ui.textEdit.toPlainText()
if not len(filepath):
return
if self.clientConnection == None:
QMessageBox.warning(self,'警告', '未有客户端连接。')
self.ui.textEdit.clear()
return
if self.ui.label_3.text() == '未打开服务器':
QMessageBox.warning(self, '警告', '未打开服务器。')
self.ui.textEdit.clear()
return
if self.status != eSTATUS.ST_IDLE:
QMessageBox.warning(self, "警告", "正在输入文件。")
self.ui.textEdit.clear()
return
print('服务端选择发送的是:', filepath)
file_path = filepath[8:]
print(file_path)
if not QFileInfo(file_path).isFile():
print('发送的不是文件。')
QMessageBox.warning(self, '警告', "请选择单个文件。")
self.ui.textEdit.clear()
return
self.sendFile(file_path)
self.ui.textEdit.clear()
def do_connect(self): #测试OK!!
self.clientConnection = self.tcpServer.nextPendingConnection() #将下一个挂起的连接作为已连接的QTcpSocket对象返回。
self.clientConnection.disconnected.connect(self.do_disconnect)
self.clientConnection.readyRead.connect(self.readMessage)
self.ui.label_3.setText('客户已连接')
@pyqtSlot()
def sendFile(self, filepath): # 测试ok!!
self.localFile = QFile(filepath)
print(type(self.localFile.read(64)))
file_info = QFileInfo(filepath)
file_name = file_info.fileName()
self.filename = file_name
if not self.localFile.open(QIODevice.ReadOnly):
print('服务端无法读取发送文件')
return
else:
temp = '正在发送'+file_name
self.ui.label_3.setText(temp)
print(f'服务端开始发送文件: {file_name} 大小:{self.localFile.size()}')
# 开始发送文件
self.bytesTowrite = self.totalBytes = self.localFile.size() # 总字节数
self.loadSize = 64*1024
self.status = eSTATUS.ST_SENDING # 线程状态
self.counter.start() #开始计时
self.timer.start(1000) #计时器工作
fileinfo = "fliename\ %s\ filze\ %d" % (file_name, self.totalBytes)
fileinfoBytes = fileinfo.encode("utf-8")
#zijie = len(fileinfoBytes)
Startframe = "@newfile\ %0.4d" % (len(fileinfoBytes))
StartframeBytes = Startframe.encode("utf-8")
print(fileinfo)
print(Startframe)
# 先发送文件信息
size1 = self.clientConnection.write(StartframeBytes)
size2 = self.clientConnection.write(fileinfoBytes)
print(size1, size2)
self.clientConnection.bytesWritten.connect(self.updateClientProgress) # 更新客户端进程
def updateClientProgress(self):
if self.bytesTowrite > 0:
self.outblock = self.localFile.read(min(self.bytesTowrite, self.loadSize))
self.bytesWriten = self.clientConnection.write(self.outblock)
self.bytesTowrite -= self.bytesWriten
self.numBytes += self.bytesWriten
setRate = int((self.numBytes / self.totalBytes) * 100)
self.ui.progressBar.setValue(setRate)
else:
self.second_task()
self.timer.stop()
self.totalBytes = 0
self.bytesWriten = 0
self.bytesTowrite = 0
self.status = eSTATUS.ST_IDLE
self.localFile.close()
self.ui.progressBar.setValue(100)
self.ui.label_3.setText('发送成功')
print(f'{self.filename}文件发送成功')
self.clientConnection.bytesWritten.disconnect(self.updateClientProgress)
客户端
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QMainWindow
from PyQt5.QtCore import QIODevice, QFile, QFileInfo, QDir, QTime, QTimer, QByteArray, pyqtSlot
from PyQt5.QtNetwork import QTcpServer, QTcpSocket, QHostAddress
from Ui_client import ClientWindow
class eSTATUS:
ST_IDLE = 0
ST_SENDING = 1
ST_RECING = 2
class MyClient(QMainWindow):
def __init__(self):
super(MyClient, self).__init__()
self.ui = ClientWindow()
self.ui.setupUi(self)
self.tcpClient = QTcpSocket()
self.tcpClient.readyRead.connect(self.readMessage)
self.tcpClient.disconnected.connect(self.do_disconnect)
self.tcpClient.error.connect(self.ErrorOccurred)
@pyqtSlot()
def on_pushButton_clicked(self):
if self.ui.pushButton.text()=='断开连接':
self.do_disconnect()
else: #建立连接
hostAddress = QHostAddress(self.ui.lineEdit.text())
tcpPort = int(self.ui.lineEdit_2.text())
self.tcpClient.abort()
self.tcpClient.connectToHost(hostAddress, tcpPort)
self.ui.label_4.setText('已连接服务器')
self.ui.pushButton.setText('断开连接')
self.ui.lineEdit.setDisabled(True)
self.ui.lineEdit_2.setDisabled(True)
self.ui.textEdit.setDisabled(False)
@pyqtSlot()
def readMessage(self):
useTime = self.count.elapsed()
if self.status == eSTATUS.ST_IDLE:
FrameHead = self.tcpClient.read(14).decode('utf-8').strip()
print(FrameHead)
filelist = FrameHead.split('\ ')
print(filelist)
fileinfolen = int(filelist[1])
print(fileinfolen)
fileinfo = self.tcpClient.read(fileinfolen).decode('utf-8')
filelist1 = fileinfo.split('\ ')
print(filelist1)
self.filename = filelist1[1]
filesize = filelist1[3]
print(self.filename, filesize)
# file_new = self.fileSavePath + self.filename
self.localfile = QFile(self.filename)
self.totalBytes = int(filesize)
if not self.localfile.open(QIODevice.WriteOnly):
print('服务端文件创建失败', self.filename)
self.do_disconnect()
return
self.status = eSTATUS.ST_RECING
self.count.start()
self.timer.start(1000)
self.ui.progressBar.setValue(0)
self.ui.label_4.setText('正在接受' + self.filename)
print(f'服务端开始接收文件:{self.filename}大小:{self.totalBytes}')
return
self.bytesReceived += self.tcpClient.bytesAvailable()
self.inBock = self.tcpClient.readAll()
self.localfile.write(self.inBock)
self.inBock.resize(0)
downedRate = (self.bytesReceived / self.totalBytes) * 100
self.ui.progressBar.setValue(downedRate)
if self.bytesReceived == self.totalBytes:
self.second_task()
self.timer.stop()
self.localfile.close()
self.bytesReceived = 0
self.status = eSTATUS.ST_IDLE
self.ui.label_4.setText('接收完成。')
self.ui.progressBar.setValue(100)
print(f'接收完毕{self.localfile.fileName()}')
print(f'用时:{useTime / 1000}s')
directory = QDir()
directory.cd(self.fileSavePath) # 进入目录
self.localfile.copy(self.fileSavePath + self.filename)
self.localfile.close()
def ErrorOccurred(self):
QMessageBox.warning(self, '警告', self.tcpClient.errorString())
self.do_disconnect()
return
详细见源码https://download.youkuaiyun.com/download/qq_42307630/21074738
参考文献
[1]:https://blog.youkuaiyun.com/weixin_42653531/article/details/103880917