利用TCP、UDPsocket实现文件传输(带GUI界面)

一、套接字基本概念
套接字(Socket)是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。
二、套接字通信的基本流程
TCP socket:
在这里插入图片描述
UDP socket:
在这里插入图片描述
在这里插入图片描述
此次实验中,采用将要传输的文件切分成1024字节大小的字节块,进行分块传输。
利用UDP发包时,会有较为严重的丢包现象。原因在于没有流量控制,直接发送导致发送的频率过快,包与包之间的时间间隔太近,接收方来不及接收造成丢包。UDP的sendto函数不会线程阻塞,不像TCP中send函数那样,直到数据发送完才会return回调用函数。
解决方法是在每次发包时调用time.sleep()函数,每次发包时间隔0.05s再发,进行一个简单的流量控制。经过测试,调用sleep函数后发送正常。
三、实验代码
TCP文件传输(需安装python环境及pyqt库):

import time
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QMainWindow, QApplication, QInputDialog, QProgressBar
import os
import sys
import socket
import struct
import threading

class EmittingStream(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)  # 定义一个发送str的信号
    def write(self, text):
        self.textWritten.emit(str(text))
        QApplication.processEvents()

class Ui_child2(QMainWindow):

    def __init__(self):
        super(Ui_child2,self).__init__()
        self.setupUi(self)
        self.retranslateUi(self)
        sys.stdout = EmittingStream()
        sys.stdout.textWritten.connect(self.outputWritten)

    def outputWritten(self, text):      # 接收信号str的信号槽
        cursor = self.textEdit.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertText(text)
        self.textEdit.setTextCursor(cursor)
        self.textEdit.ensureCursorVisible()

    def setupUi(self, child2):
        child2.setObjectName("child2")
        child2.resize(806, 430)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(20)
        child2.setFont(font)
        self.centralwidget = QtWidgets.QWidget(child2)
        self.centralwidget.setObjectName("centralwidget")

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(190, 330, 171, 61))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(20)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")

        self.pushButton1 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton1.setGeometry(QtCore.QRect(400, 330, 171, 61))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(20)
        self.pushButton1.setFont(font)
        self.pushButton1.setObjectName("pushButton1")

        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setGeometry(QtCore.QRect(10, 145, 750, 120))
        self.textEdit.setObjectName("textEdit")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.textEdit.setFont(font)
        self.verticalScrollBar1 = QtWidgets.QScrollBar(self.centralwidget)
        self.verticalScrollBar1.setGeometry(QtCore.QRect(760, 145, 25, 120))
        self.verticalScrollBar1.setOrientation(QtCore.Qt.Vertical)
        self.verticalScrollBar1.setObjectName("verticalScrollBar1")

        # 添加“IP地址”标签
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(10, 108, 100, 20))
        self.label.setObjectName("label")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.label.setFont(font)
        # 添加输入IP地址的文本框
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(110, 103, 200, 28))
        self.lineEdit.setObjectName("lineEdit")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.lineEdit.setFont(font)

        # 载入进度条控件
        self.pgb = QProgressBar(self.centralwidget)
        self.pgb.setGeometry(QtCore.QRect(10, 280, 750, 30))
        self.pgb.setObjectName("pgb")
        # 配置一个值表示进度条的当前进度
        self.pv = 0
        self.pgb.setValue(self.pv)

        # 添加“端口”标签
        self.label1 = QtWidgets.QLabel(self.centralwidget)
        self.label1.setGeometry(QtCore.QRect(350, 108, 100, 20))
        self.label1.setObjectName("label1")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.label1.setFont(font)
        # 添加输入端口的文本框
        self.lineEdit1 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit1.setGeometry(QtCore.QRect(425, 103, 200, 28))
        self.lineEdit1.setObjectName("lineEdit1")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.lineEdit1.setFont(font)

        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(10, 20, 750, 70))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(10)
        self.listWidget.setFont(font)
        self.listWidget.setObjectName("listWidget")
        self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget)
        self.verticalScrollBar.setGeometry(QtCore.QRect(760, 20, 25, 70))
        self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical)
        self.verticalScrollBar.setObjectName("verticalScrollBar")
        child2.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(child2)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 806, 28))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(11)
        self.menubar.setFont(font)
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        self.menu_2 = QtWidgets.QMenu(self.menubar)
        self.menu_2.setObjectName("menu_2")
        child2.setMenuBar(self.menubar)
        self.action_4 = QtWidgets.QAction(child2)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(11)
        self.action_4.setFont(font)
        self.action_4.setObjectName("action_4")
        self.actionh = QtWidgets.QAction(child2)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(11)
        self.actionh.setFont(font)
        self.actionh.setObjectName("actionh")
        self.actiona = QtWidgets.QAction(child2)
        self.actiona.setObjectName("actiona")
        self.menu.addAction(self.action_4)
        self.menu_2.addAction(self.actionh)
        self.menubar.addAction(self.menu.menuAction())
        self.menubar.addAction(self.menu_2.menuAction())
        self.retranslateUi(child2)
        QtCore.QMetaObject.connectSlotsByName(child2)



        self.lineEdit.returnPressed.connect(self.getip)
        self.lineEdit1.returnPressed.connect(self.getport)
        self.actionh.triggered.connect(self.shuoming)
        self.action_4.triggered.connect(self.searchfile)
        self.pushButton.clicked.connect(self.trans)
        self.pushButton1.clicked.connect(self.server)



    def retranslateUi(self, child2):
        _translate = QtCore.QCoreApplication.translate
        child2.setWindowTitle(_translate("child2", "Tcp文件传输"))
        self.pushButton.setText(_translate("child2", "发  送"))
        self.pushButton1.setText(_translate("child2", "接  收"))
        self.menu.setTitle(_translate("child2", "文件(File)"))
        self.menu_2.setTitle(_translate("child2", "帮助(Help)"))
        self.action_4.setText(_translate("child2", "选择文件"))
        self.actionh.setText(_translate("child2", "操作说明"))
        self.label.setText(_translate("child2", "IP地址:"))
        self.label1.setText(_translate("child2", "端口:"))

    def getip(self):
        # 弹出可以输入字符串的输入框
        name, ok = QInputDialog.getText(self, "IP地址", "请输入IP地址:", QtWidgets.QLineEdit.Normal)
        if ok:  # 判断是否单击了OK按钮
            self.lineEdit.setText(name)  # 获取输入对话框中的字符串,显示在文本框中

    def getport(self):
        # 弹出可以输入字符串的输入框
        name, ok = QInputDialog.getText(self, "端口", "请输入端口号:", QtWidgets.QLineEdit.Normal)
        if ok:  # 判断是否单击了OK按钮
            self.lineEdit1.setText(name)  # 获取输入对话框中的字符串,显示在文本框中

    def insertlistbox(self, text=''):
        if text != '':
            self.item = QtWidgets.QListWidgetItem(self.listWidget)  # 创建列表项
            self.item.setText(text)  # 设置项文本

    def shuoming(self):
        QtWidgets.QMessageBox.information(self, '操作说明', '操作说明:\n1、打开程序\n2、\
点击文件->选择文件(可多选)\n3、点击发送\n4、要接收对端文件须先点击接收', QMessageBox.Ok)

    def searchfile(self):
        files, filtype = QFileDialog.getOpenFileNames(self, '选择文件', 'C:\\')
        for filename in files:
            self.insertlistbox(filename)

    def client(self, ip, port):
        w = []  # 获取listwidget中条目数
        count = self.listWidget.count()  # 遍历listwidget中的内容
        for i in range(count):
            w.append(self.listWidget.item(i).text())
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((ip, port))  # 服务器连接
            filepath = ''.join(w)  # 文件传输路径

            if os.path.isfile(filepath):  # 判断是否为文件
                # 定义定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小
                fileinfo_size = struct.calcsize('128sl')
                # 定义文件头信息,包含文件名和文件大小
                fhead = struct.pack('128sl', os.path.basename(filepath).encode('utf-8'), os.stat(filepath).st_size)
                # 发送文件名称与文件大小
                s.send(fhead)
                total = os.stat(filepath).st_size
                self.pgb.setMinimum(0)
                self.pgb.setMaximum(total)  # 设置进度条的范围
                fp = open(filepath,'rb')
                print('{0}文件发送开始'.format(os.path.basename(filepath)))
                pb = 0

                while True:
                    data = fp.read(1024)
                    if not data:
                        print ('{0}文件发送完毕'.format(os.path.basename(filepath)))
                        break
                    s.send(data)
                    time.sleep(0.03)
                    pb = pb + len(data)
                    self.pgb.setValue(pb)
                s.close()

    def trans(self):
        i = self.lineEdit.text()
        p = int(self.lineEdit1.text())

        self.g = threading.Thread(target=self.client, args=(i, p))
        self.g.setDaemon(True)
        self.g.start()


    def server(self):
        hostname = socket.gethostname()
        ip = socket.gethostbyname(hostname)  # 获取本机IP
        port = 9999
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((ip, port))
        sock.listen(128)
        print("服务器{}正在监听......".format(sock.getsockname()))
        newsock, address = sock.accept()

        self.t = threading.Thread(target=self.deal_data, args=(newsock, address))
        self.t.setDaemon(True)
        self.t.start()

    def deal_data(self, conn, addr):
        while True:
            fileinfo_size = struct.calcsize('128sl')  # 申请相同大小的空间存放发送过来的文件名与文件大小信息
            buf = conn.recv(fileinfo_size)  # 接收文件名与文件大小信息
            if buf:  # 判断是否接收到文件头信息
                filename, filesize = struct.unpack('128sl', buf)  # 获取文件名和文件大小
                fn = filename.strip(b'\00')
                fn = fn.decode()
                print('文件名是{0}, 文件大小是{1}字节'.format(str(fn), filesize))
                recvd_size = 0  # 定义已接收文件的大小
                fp = open('./' + str(fn), 'wb')  # 存储在该脚本所在目录下面
                total = filesize
                self.pgb.setMinimum(0)
                self.pgb.setMaximum(total)  # 设置进度条的范围
                print("文件接收开始")

                while not recvd_size == filesize:  # 将分批次传输的二进制流依次写入到文件
                    if filesize - recvd_size > 1024:
                        data = conn.recv(1024)
                        recvd_size += len(data)
                        self.pgb.setValue(recvd_size)
                    else:
                        data = conn.recv(filesize - recvd_size)
                        recvd_size = filesize
                        self.pgb.setValue(recvd_size)
                    fp.write(data)
                fp.close()
                print('文件接收完成')
            # 传输结束断开连接
            conn.close()
            break

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()  # 创建窗体对象
    ui = Ui_child2()  # 创建PyQt5设计的窗体对象
    ui.setupUi(MainWindow)  # 调用PyQt5窗体的方法对窗体对象进行初始化设置
    MainWindow.show()  # 显示窗体
    sys.exit(app.exec_())  # 程序关闭时退出进程

UDP文件传输(需安装python环境及pyqt库):

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QMainWindow, QApplication, QInputDialog, QProgressBar
import os
import sys
import socket
import struct
import threading
import time

class EmittingStream(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)  # 定义一个发送str的信号
    def write(self, text):
        self.textWritten.emit(str(text))
        QApplication.processEvents()

class Ui_child2(QMainWindow):

    def __init__(self):
        super(Ui_child2, self).__init__()
        self.setupUi(self)
        self.retranslateUi(self)
        sys.stdout = EmittingStream()
        sys.stdout.textWritten.connect(self.outputWritten)

    def outputWritten(self, text):      # 接收信号str的信号槽
        cursor = self.textEdit.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertText(text)
        self.textEdit.setTextCursor(cursor)
        self.textEdit.ensureCursorVisible()

    def setupUi(self, child2):
        child2.setObjectName("child2")
        child2.resize(806, 430)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(20)
        child2.setFont(font)
        self.centralwidget = QtWidgets.QWidget(child2)
        self.centralwidget.setObjectName("centralwidget")

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(190, 330, 171, 61))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(20)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")

        self.pushButton1 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton1.setGeometry(QtCore.QRect(400, 330, 171, 61))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(20)
        self.pushButton1.setFont(font)
        self.pushButton1.setObjectName("pushButton1")

        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setGeometry(QtCore.QRect(10, 145, 750, 120))
        self.textEdit.setObjectName("textEdit")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.textEdit.setFont(font)
        self.verticalScrollBar1 = QtWidgets.QScrollBar(self.centralwidget)
        self.verticalScrollBar1.setGeometry(QtCore.QRect(760, 145, 25, 120))
        self.verticalScrollBar1.setOrientation(QtCore.Qt.Vertical)
        self.verticalScrollBar1.setObjectName("verticalScrollBar1")

        # 添加“IP地址”标签
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(10, 108, 100, 20))
        self.label.setObjectName("label")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.label.setFont(font)
        # 添加输入IP地址的文本框
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(110, 103, 200, 28))
        self.lineEdit.setObjectName("lineEdit")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.lineEdit.setFont(font)

        # 添加“端口”标签
        self.label1 = QtWidgets.QLabel(self.centralwidget)
        self.label1.setGeometry(QtCore.QRect(350, 108, 100, 20))
        self.label1.setObjectName("label1")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.label1.setFont(font)
        # 添加输入端口的文本框
        self.lineEdit1 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit1.setGeometry(QtCore.QRect(425, 103, 200, 28))
        self.lineEdit1.setObjectName("lineEdit1")
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.lineEdit1.setFont(font)

        # 载入进度条控件
        self.pgb = QProgressBar(self.centralwidget)
        self.pgb.setGeometry(QtCore.QRect(10, 280, 750, 30))
        self.pgb.setObjectName("pgb")
        # 配置一个值表示进度条的当前进度
        self.pv = 0
        self.pgb.setValue(self.pv)

        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(10, 20, 750, 70))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(10)
        self.listWidget.setFont(font)
        self.listWidget.setObjectName("listWidget")
        self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget)
        self.verticalScrollBar.setGeometry(QtCore.QRect(760, 20, 25, 70))
        self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical)
        self.verticalScrollBar.setObjectName("verticalScrollBar")
        child2.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(child2)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 806, 28))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(11)
        self.menubar.setFont(font)
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        self.menu_2 = QtWidgets.QMenu(self.menubar)
        self.menu_2.setObjectName("menu_2")
        child2.setMenuBar(self.menubar)
        self.action_4 = QtWidgets.QAction(child2)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(11)
        self.action_4.setFont(font)
        self.action_4.setObjectName("action_4")
        self.actionh = QtWidgets.QAction(child2)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(11)
        self.actionh.setFont(font)
        self.actionh.setObjectName("actionh")
        self.actiona = QtWidgets.QAction(child2)
        self.actiona.setObjectName("actiona")
        self.menu.addAction(self.action_4)
        self.menu_2.addAction(self.actionh)
        self.menubar.addAction(self.menu.menuAction())
        self.menubar.addAction(self.menu_2.menuAction())
        self.retranslateUi(child2)
        QtCore.QMetaObject.connectSlotsByName(child2)


        self.lineEdit.returnPressed.connect(self.getip)
        self.lineEdit1.returnPressed.connect(self.getport)
        self.actionh.triggered.connect(self.shuoming)
        self.action_4.triggered.connect(self.searchfile)
        self.pushButton.clicked.connect(self.trans)
        self.pushButton1.clicked.connect(self.server)

    def retranslateUi(self, child2):
        _translate = QtCore.QCoreApplication.translate
        child2.setWindowTitle(_translate("child2", "Udp文件传输"))
        self.pushButton.setText(_translate("child2", "发  送"))
        self.pushButton1.setText(_translate("child2", "接  收"))
        self.menu.setTitle(_translate("child2", "文件(File)"))
        self.menu_2.setTitle(_translate("child2", "帮助(Help)"))
        self.action_4.setText(_translate("child2", "选择文件"))
        self.actionh.setText(_translate("child2", "操作说明"))
        self.label.setText(_translate("child2", "IP地址:"))
        self.label1.setText(_translate("child2", "端口:"))

    def getip(self):
        # 弹出可以输入字符串的输入框
        name, ok = QInputDialog.getText(self, "IP地址", "请输入IP地址:", QtWidgets.QLineEdit.Normal)
        if ok:  # 判断是否单击了OK按钮
            self.lineEdit.setText(name)  # 获取输入对话框中的字符串,显示在文本框中

    def getport(self):
        # 弹出可以输入字符串的输入框
        name, ok = QInputDialog.getText(self, "端口", "请输入端口号:", QtWidgets.QLineEdit.Normal)
        if ok:  # 判断是否单击了OK按钮
            self.lineEdit1.setText(name)  # 获取输入对话框中的字符串,显示在文本框中

    def insertlistbox(self, text=''):
        if text != '':
            self.item = QtWidgets.QListWidgetItem(self.listWidget)  # 创建列表项
            self.item.setText(text)  # 设置项文本

    def shuoming(self):
        QtWidgets.QMessageBox.information(self, '操作说明', '操作说明:\n1、打开程序\n2、\
点击文件->选择文件(可多选)\n3、点击发送\n4、要接收对端文件须先点击接收', QMessageBox.Ok)

    def searchfile(self):
        files, filtype = QFileDialog.getOpenFileNames(self, '选择文件', 'C:\\')
        for filename in files:
            self.insertlistbox(filename)

    def client(self, ip, port):
        w = []  # 获取listwidget中条目数
        count = self.listWidget.count()  # 遍历listwidget中的内容
        for i in range(count):
            w.append(self.listWidget.item(i).text())
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect((ip, port))
            filepath = ''.join(w)  # 文件传输路径

            if os.path.isfile(filepath):  # 判断是否为文件
                # 定义定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小
                fileinfo_size = struct.calcsize('128sl')
                # 定义文件头信息,包含文件名和文件大小
                fhead = struct.pack('128sl', os.path.basename(filepath).encode('utf-8'), os.stat(filepath).st_size)
                # 发送文件名称与文件大小
                s.send(fhead)
                total = os.stat(filepath).st_size
                self.pgb.setMinimum(0)
                self.pgb.setMaximum(total)  # 设置进度条的范围
                fp = open(filepath, 'rb')
                print('{0}文件发送开始'.format(os.path.basename(filepath)))
                pb = 0

                while True:
                    data = fp.read(1024)
                    if not data:
                        print('{0}文件发送完毕'.format(os.path.basename(filepath)))
                        break
                    time.sleep(0.05)
                    s.send(data)
                    pb = pb + len(data)
                    self.pgb.setValue(pb)
                s.close()

    def trans(self):
        i = self.lineEdit.text()
        p = int(self.lineEdit1.text())

        self.g = threading.Thread(target=self.client, args=(i, p))
        self.g.setDaemon(True)
        self.g.start()

    def server(self):
        hostname = socket.gethostname()
        ip = socket.gethostbyname(hostname)  # 获取本机IP
        port = 9999
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((ip, port))
        print("服务器{}正在监听......".format(sock.getsockname()))

        self.t = threading.Thread(target=self.deal_data, args=(sock,))
        self.t.setDaemon(True)
        self.t.start()

    def deal_data(self, conn):
        while True:
            fileinfo_size = struct.calcsize('128sl')  # 申请相同大小的空间存放发送过来的文件名与文件大小信息
            buf, addr = conn.recvfrom(fileinfo_size)  # 接收文件名与文件大小信息
            if buf:  # 判断是否接收到文件头信息
                filename, filesize = struct.unpack('128sl', buf)  # 获取文件名和文件大小
                fn = filename.strip(b'\00')
                fn = fn.decode()
                print('文件名是{0}, 文件大小是{1}字节'.format(str(fn), filesize))
                recvd_size = 0  # 定义已接收文件的大小
                fp = open('./' + str(fn), 'wb')  # 存储在该脚本所在目录下面
                total = filesize
                self.pgb.setMinimum(0)
                self.pgb.setMaximum(total)  # 设置进度条的范围
                print("文件接收开始")
                while not recvd_size == filesize:  # 将分批次传输的二进制流依次写入到文件
                    if filesize - recvd_size > 1024:
                        data, addr = conn.recvfrom(1024)
                        recvd_size += len(data)
                        self.pgb.setValue(recvd_size)
                    else:
                        data, addr = conn.recvfrom(filesize - recvd_size)
                        recvd_size = filesize
                        self.pgb.setValue(recvd_size)
                    fp.write(data)
                fp.close()
                print('文件接收完成')
            # 传输结束断开连接
            conn.close()
            break

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()  # 创建窗体对象
    ui = Ui_child2()  # 创建PyQt5设计的窗体对象
    ui.setupUi(MainWindow)  # 调用PyQt5窗体的方法对窗体对象进行初始化设置
    MainWindow.show()  # 显示窗体
    sys.exit(app.exec_())  # 程序关闭时退出进程

四、实验结果截图
实验验证中,构建了了一个Vmware虚拟机,实现物理机与虚拟机之间的文件传输。虚拟机的IP地址为192.168.0.5,物理机IP为192.168.0.1。经测试,实验成功。实验结果截图如下所示:
TCP文件传输:
在这里插入图片描述
UDP文件传输:
在这里插入图片描述


版权声明:本文为weixin_48661059原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。