stopServer

Tomcat启动异常解析
2007-8-22 16:42:49 org.apache.catalina.startup.Catalina stopServer
严重: Catalina.stop:
java.net.ConnectException: Connection refused: connect
 at java.net.PlainSocketImpl.socketConnect(Native Method)
 at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
 at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
 at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
 at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:364)
 at java.net.Socket.connect(Socket.java:507)
 at java.net.Socket.connect(Socket.java:457)
 at java.net.Socket.<init>(Socket.java:365)
 at java.net.Socket.<init>(Socket.java:178)
 at org.apache.catalina.startup.Catalina.stopServer(Catalina.java:395)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:585)
 at org.apache.catalina.startup.Bootstrap.stopServer(Bootstrap.java:320)
 at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:411)
================================================================================ 文件传输系统 - 服务器端代码 ================================================================================ [文件: WinServ.pro] QT += core gui network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main_server.cpp \ serverwidget.cpp HEADERS += \ serverwidget.h FORMS += \ serverwidget.ui RESOURCES += \ resources.qrc RC_FILE = app.rc # Build directory DESTDIR = bin OBJECTS_DIR = temp/obj MOC_DIR = temp/moc RCC_DIR = temp/rcc UI_DIR = temp/ui [文件: main_server.cpp] #include "serverwidget.h" #include <QApplication> #include <QTextCodec> int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(5,0,0) QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); #endif QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QApplication a(argc, argv); ServerWidget w; w.setFixedSize(562, 400); w.show(); return a.exec(); } [文件: serverwidget.h] #ifndef SERVERWIDGET_H #define SERVERWIDGET_H #include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QFile> #include <QDir> #include <QMap> #include <QAtomicInteger> #include <QMenu> #include <QSystemTrayIcon> #include <QThreadPool> #include <QRunnable> #include <QLineEdit> #include <QIntValidator> namespace Ui { class ServerWidget; } struct FileInfo { QString relativePath; qint64 size; QByteArray hash; QString absolutePath; }; struct ClientInfo { QTcpSocket* socket; QMap<QString, FileInfo> requestedFiles; int concurrentTransfers; QAtomicInteger<int> activeTransfers; bool isReceivingFileList; QByteArray fileListBuffer; }; class FileTransferTask : public QRunnable { public: FileTransferTask(QTcpSocket* socket, const FileInfo& fileInfo, QObject* parent = nullptr); void run() override; private: QTcpSocket* m_socket; FileInfo m_fileInfo; QObject* m_parent; }; class ServerWidget : public QWidget { Q_OBJECT public: explicit ServerWidget(QWidget *parent = nullptr); ~ServerWidget(); protected: void closeEvent(QCloseEvent *event) override; void changeEvent(QEvent *event) override; private slots: void on_starButton_clicked(); void on_stopButton_clicked(); void DirButton_clicked(); void on_radioButton_clicked(); void onConcurrentTextChanged(const QString &text); void newConnection(); void clientDisconnected(); void readClientData(); void onBytesWritten(qint64 bytes); void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason); void onRestoreAction(); void onQuitAction(); private: Ui::ServerWidget *ui; QTcpServer *tcpServer; QMap<QTcpSocket*, ClientInfo*> clients; QString serverDir; QFile *logFile; QMenu *contextMenu; QSystemTrayIcon *trayIcon; QThreadPool *threadPool; QMap<QString, FileInfo> fileList; int maxConcurrentTransfers; QIntValidator *concurrentValidator; void showLog(const QString &message); void initContextMenu(); void showContextMenu(const QPoint &pos); void clearLog(); void copyText(); void initSystemTray(); QString getLocalIP(); void initLogFile(); void closeLogFile(); void loadPortSetting(); void savePortSetting(); void scanDirectory(); QByteArray calculateFileHash(const QString &filePath); void sendFileList(QTcpSocket* socket); void cleanupClient(ClientInfo* clientInfo); void stopServer(); void closeServer(); void sendErrorResponse(QTcpSocket* socket, const QString& errorMessage); void handleFileRequest(QTcpSocket* socket, const QString& filePath); }; #endif [文件: serverwidget.cpp] #include "serverwidget.h" #include "ui_serverwidget.h" #include <QNetworkInterface> #include <QFileDialog> #include <QMessageBox> #include <QCryptographicHash> #include <QDateTime> #include <QHostAddress> #include <QTextCursor> #include <QCloseEvent> #include <QTimer> #include <QSettings> #include <QClipboard> #include <QSystemTrayIcon> #include <QMenu> #include <QAction> #include <QThreadPool> #include <QStack> #include <QLineEdit> #include <QIntValidator> FileTransferTask::FileTransferTask(QTcpSocket* socket, const FileInfo& fileInfo, QObject* parent) : m_socket(socket), m_fileInfo(fileInfo), m_parent(parent) { setAutoDelete(true); } void FileTransferTask::run() { if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState) { return; } QFile file(m_fileInfo.absolutePath); if (!file.open(QIODevice::ReadOnly)) { return; } QByteArray header = "FILE:" + m_fileInfo.relativePath.toUtf8() + "|" + QByteArray::number(m_fileInfo.size) + "|" + m_fileInfo.hash.toHex() + "\n"; m_socket->write(header); if (!m_socket->waitForBytesWritten(5000)) { file.close(); return; } const qint64 chunkSize = 64 * 1024; qint64 bytesSent = 0; while (!file.atEnd() && bytesSent < m_fileInfo.size) { QByteArray data = file.read(chunkSize); qint64 bytesWritten = m_socket->write(data); if (bytesWritten == -1) { break; } if (!m_socket->waitForBytesWritten(30000)) { break; } bytesSent += bytesWritten; } file.close(); m_socket->write("FILE_END\n"); m_socket->waitForBytesWritten(5000); } ServerWidget::ServerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ServerWidget), tcpServer(nullptr), logFile(nullptr), trayIcon(nullptr), threadPool(new QThreadPool(this)), maxConcurrentTransfers(3), concurrentValidator(new QIntValidator(1, 512, this)) { setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint); ui->setupUi(this); threadPool->setMaxThreadCount(maxConcurrentTransfers); ui->lineEditConcurrent->setValidator(concurrentValidator); ui->lineEditConcurrent->setText(QString::number(maxConcurrentTransfers)); connect(ui->lineEditConcurrent, &QLineEdit::textChanged, this, &ServerWidget::onConcurrentTextChanged); ui->line_IP->setText(getLocalIP()); ui->line_IP->setEnabled(false); ui->line_Port->setEnabled(false); QString downPath = QDir::currentPath() + "/FileDownload"; QDir dir; if (!dir.exists(downPath)) { dir.mkdir(downPath); } ui->line_Dir->setText(downPath); serverDir = downPath; loadPortSetting(); connect(ui->DirButton, &QPushButton::clicked, this, &ServerWidget::DirButton_clicked); ui->textBrowser_Show->setFont(QFont("Consolas", 9)); initLogFile(); initContextMenu(); initSystemTray(); showLog("服务器初始化完成"); } ServerWidget::~ServerWidget() { stopServer(); closeLogFile(); if (trayIcon) { trayIcon->hide(); delete trayIcon; } threadPool->waitForDone(3000); delete ui; } void ServerWidget::onConcurrentTextChanged(const QString &text) { bool ok; int value = text.toInt(&ok); if (ok && value >= 1 && value <= 512) { maxConcurrentTransfers = value; threadPool->setMaxThreadCount(value); showLog("并发传输数已设置为: " + QString::number(value)); } else if (!text.isEmpty()) { ui->lineEditConcurrent->setText(QString::number(maxConcurrentTransfers)); } } void ServerWidget::scanDirectory() { fileList.clear(); QDir dir(serverDir); if (!dir.exists()) { showLog("错误: 目录不存在: " + serverDir); return; } QStack<QDir> dirStack; dirStack.push(dir); int fileCount = 0; qint64 totalSize = 0; while (!dirStack.isEmpty()) { QDir currentDir = dirStack.pop(); QFileInfoList entries = currentDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); for (const QFileInfo &entry : entries) { if (entry.isDir()) { dirStack.push(QDir(entry.absoluteFilePath())); } else { FileInfo fileInfo; fileInfo.relativePath = dir.relativeFilePath(entry.absoluteFilePath()); fileInfo.absolutePath = entry.absoluteFilePath(); fileInfo.size = entry.size(); fileInfo.hash = calculateFileHash(entry.absoluteFilePath()); if (!fileInfo.hash.isEmpty()) { fileList.insert(fileInfo.relativePath, fileInfo); fileCount++; totalSize += entry.size(); } } } } double totalSizeMB = totalSize / (1024.0 * 1024.0); showLog(QString("目录扫描完成,共发现 %1 个文件,总大小: %2 MB").arg(fileCount).arg(totalSizeMB, 0, 'f', 2)); } void ServerWidget::sendFileList(QTcpSocket* socket) { if (!socket || socket->state() != QAbstractSocket::ConnectedState) { return; } if (fileList.isEmpty()) { sendErrorResponse(socket, "服务器文件列表为空"); return; } showLog("向客户端发送文件列表..."); QByteArray header = "FILE_LIST_START:" + QByteArray::number(fileList.size()) + "\n"; qint64 written = socket->write(header); if (written == -1 || !socket->waitForBytesWritten(5000)) { showLog("发送文件列表头失败"); return; } int filesSent = 0; for (auto it = fileList.constBegin(); it != fileList.constEnd(); ++it) { const FileInfo &fileInfo = it.value(); QByteArray fileData = fileInfo.relativePath.toUtf8() + "|" + QByteArray::number(fileInfo.size) + "|" + fileInfo.hash.toHex() + "\n"; written = socket->write(fileData); if (written == -1) { showLog("发送文件信息失败: " + fileInfo.relativePath); continue; } if (!socket->waitForBytesWritten(1000)) { showLog("发送文件信息超时: " + fileInfo.relativePath); break; } filesSent++; } socket->write("FILE_LIST_END\n"); socket->waitForBytesWritten(5000); showLog(QString("文件列表发送完成,成功发送 %1/%2 个文件信息").arg(filesSent).arg(fileList.size())); } void ServerWidget::newConnection() { QTcpSocket *clientSocket = tcpServer->nextPendingConnection(); if (!clientSocket) return; clientSocket->setReadBufferSize(256 * 1024); clientSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1); ClientInfo *clientInfo = new ClientInfo; clientInfo->socket = clientSocket; clientInfo->concurrentTransfers = maxConcurrentTransfers; clientInfo->activeTransfers.storeRelaxed(0); clientInfo->isReceivingFileList = false; clientInfo->fileListBuffer.clear(); clients.insert(clientSocket, clientInfo); connect(clientSocket, &QTcpSocket::disconnected, this, &ServerWidget::clientDisconnected); connect(clientSocket, &QTcpSocket::readyRead, this, &ServerWidget::readClientData); connect(clientSocket, &QTcpSocket::bytesWritten, this, &ServerWidget::onBytesWritten); QString clientIP = clientSocket->peerAddress().toString(); if (clientIP.startsWith("::ffff:")) clientIP = clientIP.mid(7); quint16 clientPort = clientSocket->peerPort(); showLog("客户端连接: " + clientIP + ":" + QString::number(clientPort)); scanDirectory(); sendFileList(clientSocket); } void ServerWidget::readClientData() { QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender()); if (!clientSocket || !clients.contains(clientSocket)) return; ClientInfo *clientInfo = clients.value(clientSocket); QByteArray data = clientSocket->readAll(); QList<QByteArray> lines = data.split('\n'); for (const QByteArray &line : lines) { if (line.isEmpty()) continue; QString request = QString::fromUtf8(line).trimmed(); if (request.startsWith("REQUEST_FILE:")) { QString filePath = request.mid(13); handleFileRequest(clientSocket, filePath); } else if (request == "TRANSFER_COMPLETE") { clientInfo->activeTransfers.deref(); } else if (request == "ALL_FILES_COMPLETE") { QString clientIP = clientSocket->peerAddress().toString(); if (clientIP.startsWith("::ffff:")) clientIP = clientIP.mid(7); showLog("客户端 " + clientIP + " 所有文件传输完成"); } else if (request == "GET_FILE_LIST") { showLog("客户端请求更新文件列表"); scanDirectory(); sendFileList(clientSocket); } else if (!request.isEmpty()) { showLog("未知命令: " + request); } } } void ServerWidget::handleFileRequest(QTcpSocket* socket, const QString& filePath) { if (!socket || !clients.contains(socket)) return; ClientInfo *clientInfo = clients.value(socket); if (fileList.contains(filePath)) { FileInfo fileInfo = fileList.value(filePath); if (clientInfo->activeTransfers.loadRelaxed() >= maxConcurrentTransfers) { sendErrorResponse(socket, "并发传输数已达上限: " + QString::number(maxConcurrentTransfers)); return; } QFileInfo diskFileInfo(fileInfo.absolutePath); if (!diskFileInfo.exists() || !diskFileInfo.isFile()) { sendErrorResponse(socket, "文件不存在: " + filePath); showLog("文件不存在: " + fileInfo.absolutePath); return; } clientInfo->activeTransfers.ref(); clientInfo->requestedFiles.insert(filePath, fileInfo); FileTransferTask *task = new FileTransferTask(socket, fileInfo, this); threadPool->start(task); showLog("开始传输文件: " + filePath + " (" + QString::number(fileInfo.size / (1024.0 * 1024.0), 'f', 2) + " MB)"); } else { sendErrorResponse(socket, "文件不存在: " + filePath); showLog("客户端请求不存在的文件: " + filePath); } } void ServerWidget::clientDisconnected() { QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender()); if (!clientSocket) return; QString clientIP = clientSocket->peerAddress().toString(); if (clientIP.startsWith("::ffff:")) clientIP = clientIP.mid(7); if (clients.contains(clientSocket)) { ClientInfo *clientInfo = clients.value(clientSocket); if (clientInfo->activeTransfers.loadRelaxed() > 0) { showLog("客户端断开,中断 " + QString::number(clientInfo->activeTransfers.loadRelaxed()) + " 个传输任务"); } cleanupClient(clientInfo); clients.remove(clientSocket); delete clientInfo; } showLog("客户端断开连接: " + clientIP); clientSocket->deleteLater(); } void ServerWidget::onBytesWritten(qint64 bytes) { Q_UNUSED(bytes); } QString ServerWidget::getLocalIP() { QString localIP; QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); for (const QHostAddress &address : ipAddressesList) { if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress::LocalHost) { localIP = address.toString(); break; } } if (localIP.isEmpty()) { localIP = QHostAddress(QHostAddress::LocalHost).toString(); } return localIP; } void ServerWidget::loadPortSetting() { QString configPath = QDir::currentPath() + "/LocConfig"; QString filePath = configPath + "/Connset.con"; QDir dir; if (!dir.exists(configPath)) { dir.mkpath(configPath); return; } QSettings settings(filePath, QSettings::IniFormat); quint16 port = settings.value("Network/ServerPort", 15210).toUInt(); ui->line_Port->setText(QString::number(port)); } void ServerWidget::savePortSetting() { bool portOk; quint16 port = ui->line_Port->text().toUShort(&portOk); if (portOk && port > 0 && port < 65535) { QString configPath = QDir::currentPath() + "/LocConfig"; QString filePath = configPath + "/Connset.con"; QDir dir; if (!dir.exists(configPath)) { dir.mkpath(configPath); } QSettings settings(filePath, QSettings::IniFormat); settings.setValue("Network/ServerPort", port); settings.sync(); } } void ServerWidget::initLogFile() { QDir logDir(QDir::currentPath() + "/log"); if (!logDir.exists()) logDir.mkpath("."); QString logFileName = QDateTime::currentDateTime().toString("yyyy.MM.dd") + ".log"; QString logFilePath = logDir.absoluteFilePath(logFileName); logFile = new QFile(logFilePath); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { delete logFile; logFile = nullptr; showLog("无法打开日志文件:" + logFilePath); return; } QTextStream stream(logFile); QString titleText = "文件服务器日志"; stream << "\n"; stream << "**************************************************************************\n"; stream << " " + titleText << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << "\n"; stream << "**************************************************************************\n"; stream.flush(); } void ServerWidget::closeLogFile() { if (logFile) { if (logFile->isOpen()) { logFile->close(); } delete logFile; logFile = nullptr; } } void ServerWidget::showLog(const QString &message) { QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); QString logMessage = "[" + timestamp + "] " + message; if (ui->textBrowser_Show->document()->blockCount() > 1000) { QTextCursor cursor(ui->textBrowser_Show->document()); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, 100); cursor.removeSelectedText(); } ui->textBrowser_Show->append(logMessage); QTextCursor cursor = ui->textBrowser_Show->textCursor(); cursor.movePosition(QTextCursor::End); ui->textBrowser_Show->setTextCursor(cursor); if (ui->radioButton_rj->isChecked() && logFile && logFile->isOpen()) { QTextStream stream(logFile); stream << logMessage << "\n"; stream.flush(); } } void ServerWidget::DirButton_clicked() { QString dirNew = QFileDialog::getExistingDirectory(this, "选择服务器目录", QDir::currentPath(), QFileDialog::ShowDirsOnly); if (dirNew.isEmpty() || !QDir(dirNew).exists()) { QMessageBox::warning(this, "警告", "选择目录为空或无效,请重新选择目录"); return; } serverDir = QDir(dirNew).absolutePath(); ui->line_Dir->setText(serverDir); showLog("服务器目录已更改为: " + serverDir); } void ServerWidget::on_radioButton_clicked() { bool enable = ui->radioButton->isChecked(); ui->line_IP->setEnabled(enable); ui->line_Port->setEnabled(enable); } void ServerWidget::on_starButton_clicked() { if (tcpServer && tcpServer->isListening()) { closeServer(); ui->starButton->setText("启动"); showLog("服务器已停止"); ui->radioButton->setEnabled(true); ui->line_Dir->setEnabled(true); ui->DirButton->setEnabled(true); } else { bool portOk; quint16 port = ui->line_Port->text().toUShort(&portOk); if (!portOk || port <= 0 || port >= 65535) { QMessageBox::warning(this, "端口错误", "请输入有效的端口号 (1-65534)"); return; } savePortSetting(); serverDir = ui->line_Dir->text(); if (!QDir(serverDir).exists()) { QMessageBox::warning(this, "目录错误", "服务器目录不存在"); return; } showLog("正在扫描服务器目录..."); scanDirectory(); if (!tcpServer) { tcpServer = new QTcpServer(this); } if (!tcpServer->listen(QHostAddress::Any, port)) { showLog("服务器启动失败: " + tcpServer->errorString()); return; } connect(tcpServer, &QTcpServer::newConnection, this, &ServerWidget::newConnection); showLog("服务器已启动,监听端口: " + QString::number(port)); showLog("服务器目录: " + serverDir); showLog("等待客户端连接..."); ui->starButton->setText("停止"); ui->radioButton->setEnabled(false); ui->line_IP->setEnabled(false); ui->line_Port->setEnabled(false); ui->line_Dir->setEnabled(false); ui->DirButton->setEnabled(false); } } void ServerWidget::on_stopButton_clicked() { QApplication::quit(); } void ServerWidget::closeServer() { if (tcpServer && tcpServer->isListening()) { QList<QTcpSocket*> clientSockets = clients.keys(); for (QTcpSocket* socket : clientSockets) { if (clients.contains(socket)) { ClientInfo* clientInfo = clients.value(socket); socket->disconnectFromHost(); if (socket->state() == QAbstractSocket::ConnectedState) { socket->waitForDisconnected(1000); } cleanupClient(clientInfo); clients.remove(socket); delete clientInfo; } } tcpServer->close(); } } void ServerWidget::stopServer() { closeServer(); if (tcpServer) { delete tcpServer; tcpServer = nullptr; } } void ServerWidget::closeEvent(QCloseEvent *event) { stopServer(); event->accept(); } void ServerWidget::changeEvent(QEvent *event) { QWidget::changeEvent(event); if (event->type() == QEvent::WindowStateChange) { if (isMinimized()) { hide(); if (trayIcon && trayIcon->isVisible()) { trayIcon->showMessage("文件服务器", "程序已最小化到系统托盘", QSystemTrayIcon::Information, 2000); } } } } QByteArray ServerWidget::calculateFileHash(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { return QByteArray(); } QCryptographicHash hash(QCryptographicHash::Md5); const qint64 bufferSize = 64 * 1024; char buffer[bufferSize]; qint64 bytesRead; while ((bytesRead = file.read(buffer, bufferSize)) > 0) { hash.addData(buffer, bytesRead); } file.close(); return hash.result(); } void ServerWidget::cleanupClient(ClientInfo* clientInfo) { if (!clientInfo) return; clientInfo->requestedFiles.clear(); clientInfo->activeTransfers.storeRelaxed(0); clientInfo->isReceivingFileList = false; clientInfo->fileListBuffer.clear(); } void ServerWidget::sendErrorResponse(QTcpSocket* socket, const QString& errorMessage) { if (!socket || socket->state() != QAbstractSocket::ConnectedState) { return; } QByteArray errorData = "ERROR:" + errorMessage.toUtf8() + "\n"; socket->write(errorData); socket->waitForBytesWritten(1000); } void ServerWidget::initContextMenu() { contextMenu = new QMenu(this); QAction *copyAction = new QAction("复制", this); copyAction->setShortcut(QKeySequence::Copy); connect(copyAction, &QAction::triggered, this, &ServerWidget::copyText); contextMenu->addAction(copyAction); contextMenu->addSeparator(); QAction *clearAction = new QAction("清除日志", this); clearAction->setShortcut(QKeySequence("Ctrl+Del")); connect(clearAction, &QAction::triggered, this, &ServerWidget::clearLog); contextMenu->addAction(clearAction); ui->textBrowser_Show->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->textBrowser_Show, &QTextBrowser::customContextMenuRequested, this, &ServerWidget::showContextMenu); } void ServerWidget::showContextMenu(const QPoint &pos) { contextMenu->exec(ui->textBrowser_Show->mapToGlobal(pos)); } void ServerWidget::copyText() { QTextCursor cursor = ui->textBrowser_Show->textCursor(); if (cursor.hasSelection()) { QString selectedText = cursor.selectedText(); QApplication::clipboard()->setText(selectedText); } } void ServerWidget::clearLog() { ui->textBrowser_Show->clear(); } void ServerWidget::initSystemTray() { trayIcon = new QSystemTrayIcon(this); QIcon icon = QIcon(":/icon/Server-Network.ico"); if (icon.isNull()) { QPixmap pixmap(16, 16); pixmap.fill(Qt::blue); icon = QIcon(pixmap); } trayIcon->setIcon(icon); trayIcon->setToolTip("文件服务器"); QMenu *trayMenu = new QMenu(this); QAction *restoreAction = new QAction("打开", this); connect(restoreAction, &QAction::triggered, this, &ServerWidget::onRestoreAction); trayMenu->addAction(restoreAction); trayMenu->addSeparator(); QAction *quitAction = new QAction("退出", this); connect(quitAction, &QAction::triggered, this, &ServerWidget::onQuitAction); trayMenu->addAction(quitAction); trayIcon->setContextMenu(trayMenu); connect(trayIcon, &QSystemTrayIcon::activated, this, &ServerWidget::onTrayIconActivated); trayIcon->show(); } void ServerWidget::onTrayIconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::DoubleClick: onRestoreAction(); break; default: break; } } void ServerWidget::onRestoreAction() { setWindowState(windowState() & ~Qt::WindowMinimized); show(); raise(); activateWindow(); } void ServerWidget::onQuitAction() { stopServer(); closeLogFile(); QApplication::quit(); } [文件: serverwidget.ui] <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ServerWidget</class> <widget class="QWidget" name="ServerWidget"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>562</width> <height>400</height> </rect> </property> <property name="windowTitle"> <string>文件服务器</string> </property> <widget class="QLabel" name="label"> <property name="geometry"> <rect> <x>292</x> <y>6</y> <width>61</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>端口:</string> </property> </widget> <widget class="QLabel" name="label_2"> <property name="geometry"> <rect> <x>6</x> <y>6</y> <width>81</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>本机IP:</string> </property> </widget> <widget class="QPushButton" name="stopButton"> <property name="geometry"> <rect> <x>480</x> <y>290</y> <width>70</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>11</pointsize> </font> </property> <property name="text"> <string>退出</string> </property> </widget> <widget class="QLineEdit" name="line_Dir"> <property name="geometry"> <rect> <x>130</x> <y>60</y> <width>381</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>10</pointsize> </font> </property> </widget> <widget class="QRadioButton" name="radioButton"> <property name="geometry"> <rect> <x>452</x> <y>6</y> <width>101</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>14</pointsize> </font> </property> <property name="text"> <string>修改端口</string> </property> </widget> <widget class="QLineEdit" name="line_Port"> <property name="geometry"> <rect> <x>356</x> <y>6</y> <width>61</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>15210</string> </property> <property name="maxLength"> <number>5</number> </property> </widget> <widget class="QPushButton" name="starButton"> <property name="geometry"> <rect> <x>480</x> <y>220</y> <width>70</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>11</pointsize> </font> </property> <property name="text"> <string>启动</string> </property> </widget> <widget class="QLabel" name="label_3"> <property name="geometry"> <rect> <x>6</x> <y>60</y> <width>131</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>12</pointsize> </font> </property> <property name="text"> <string>服务器文件路径:</string> </property> </widget> <widget class="QLineEdit" name="line_IP"> <property name="enabled"> <bool>false</bool> </property> <property name="geometry"> <rect> <x>86</x> <y>6</y> <width>181</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>15</pointsize> </font> </property> <property name="maxLength"> <number>15</number> </property> </widget> <widget class="QTextBrowser" name="textBrowser_Show"> <property name="geometry"> <rect> <x>6</x> <y>114</y> <width>471</width> <height>231</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>12</pointsize> </font> </property> <property name="autoFillBackground"> <bool>true</bool> </property> </widget> <widget class="QPushButton" name="DirButton"> <property name="geometry"> <rect> <x>510</x> <y>60</y> <width>41</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>11</pointsize> </font> </property> <property name="text"> <string>...</string> </property> </widget> <widget class="QRadioButton" name="radioButton_rj"> <property name="geometry"> <rect> <x>490</x> <y>140</y> <width>71</width> <height>51</height> </rect> </property> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <property name="text"> <string>记录日志</string> </property> </widget> <widget class="QLabel" name="label_concurrent"> <property name="geometry"> <rect> <x>490</x> <y>100</y> <width>71</width> <height>16</height> </rect> </property> <property name="text"> <string>并发传输数:</string> </property> </widget> <widget class="QLineEdit" name="lineEditConcurrent"> <property name="geometry"> <rect> <x>490</x> <y>120</y> <width>61</width> <height>22</height> </rect> </property> <property name="text"> <string>3</string> </property> <property name="maxLength"> <number>3</number> </property> </widget> </widget> <resources/> <connections/> </ui> [文件: resources.qrc] <RCC> <qresource prefix="/icon"> <file>icons/Server-Network.ico</file> </qresource> </RCC> ================================================================================ 文件传输系统 - 客户端代码 ================================================================================ [文件: WinClient.pro] QT += core gui network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ clientwidget.cpp \ main_client.cpp HEADERS += \ clientwidget.h FORMS += \ clientwidget.ui # Build directory DESTDIR = bin OBJECTS_DIR = temp/obj MOC_DIR = temp/moc RCC_DIR = temp/rcc UI_DIR = temp/ui [文件: main_client.cpp] #include "clientwidget.h" #include <QApplication> #include <QTextCodec> int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(5,0,0) QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); #endif QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QApplication a(argc, argv); ClientWidget w; w.setFixedSize(575, 400); w.show(); return a.exec(); } [文件: clientwidget.h] #ifndef CLIENTWIDGET_H #define CLIENTWIDGET_H #include <QWidget> #include <QTcpSocket> #include <QFile> #include <QDir> #include <QMap> #include <QSettings> #include <QThreadPool> #include <QRunnable> #include <QLineEdit> #include <QIntValidator> namespace Ui { class ClientWidget; } struct FileInfo { QString relativePath; qint64 size; QByteArray hash; }; class FileDownloadTask : public QRunnable { public: FileDownloadTask(const QString& serverIP, quint16 port, const FileInfo& fileInfo, const QString& baseDir, QObject* parent = nullptr); void run() override; signals: void downloadFinished(const QString& filePath, bool success); private: QString m_serverIP; quint16 m_port; FileInfo m_fileInfo; QString m_baseDir; QObject* m_parent; }; class ClientWidget : public QWidget { Q_OBJECT public: explicit ClientWidget(QWidget *parent = nullptr); ~ClientWidget(); private slots: void on_radioButton_clicked(); void on_pushButton_clicked(); void onDownloadFinished(const QString& filePath, bool success); void onConcurrentTextChanged(const QString &text); void onSocketConnected(); void onSocketDisconnected(); void onSocketReadyRead(); void onSocketError(QAbstractSocket::SocketError error); private: Ui::ClientWidget *ui; QTcpSocket *tcpSocket; QMap<QString, FileInfo> fileList; QThreadPool *threadPool; int maxConcurrentDownloads; int completedDownloads; int totalFiles; bool isDownloading; QByteArray receiveBuffer; bool isReceivingFileList; QIntValidator *concurrentValidator; void showLog(const QString &message); void connectToServer(); void getFileListFromServer(); void parseFileList(const QByteArray& data); void startDownloads(); void resetTransferState(); void processFileListData(const QByteArray& data); bool verifyFileHash(const QString& filePath, const QByteArray& expectedHash); void saveConnectionSettings(); void loadConnectionSettings(); QString getParentDir(); }; #endif [文件: clientwidget.cpp] #include "clientwidget.h" #include "ui_clientwidget.h" #include <QTcpSocket> #include <QFileDialog> #include <QMessageBox> #include <QCryptographicHash> #include <QDateTime> #include <QHostAddress> #include <QTextCursor> #include <QThreadPool> #include <QDir> #include <QFileInfo> #include <QIntValidator> QString Encrypt(const QString& value, const QString& key) { QByteArray encryptedData; QByteArray dataToEncrypt = value.toUtf8(); QByteArray hash = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha256); for (int i = 0; i < dataToEncrypt.size(); ++i) { encryptedData.append(dataToEncrypt.at(i) ^ hash.at(i % hash.size())); } return encryptedData.toBase64(); } QString Decrypt(const QString& encryptedValue, const QString& key) { QByteArray decryptedData; QByteArray dataToDecrypt = QByteArray::fromBase64(encryptedValue.toUtf8()); QByteArray hash = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha256); for (int i = 0; i < dataToDecrypt.size(); ++i) { decryptedData.append(dataToDecrypt.at(i) ^ hash.at(i % hash.size())); } return QString::fromUtf8(decryptedData); } FileDownloadTask::FileDownloadTask(const QString& serverIP, quint16 port, const FileInfo& fileInfo, const QString& baseDir, QObject* parent) : m_serverIP(serverIP), m_port(port), m_fileInfo(fileInfo), m_baseDir(baseDir), m_parent(parent) { setAutoDelete(true); } void FileDownloadTask::run() { QTcpSocket socket; socket.connectToHost(m_serverIP, m_port); if (!socket.waitForConnected(5000)) { emit static_cast<ClientWidget*>(m_parent)->downloadFinished(m_fileInfo.relativePath, false); return; } QByteArray request = "REQUEST_FILE:" + m_fileInfo.relativePath.toUtf8() + "\n"; socket.write(request); if (!socket.waitForBytesWritten(5000)) { emit static_cast<ClientWidget*>(m_parent)->downloadFinished(m_fileInfo.relativePath, false); return; } QString filePath = QDir(m_baseDir).filePath(m_fileInfo.relativePath); QFileInfo fileInfo(filePath); QDir().mkpath(fileInfo.absolutePath()); QFile file(filePath); if (!file.open(QIODevice::WriteOnly)) { emit static_cast<ClientWidget*>(m_parent)->downloadFinished(m_fileInfo.relativePath, false); return; } qint64 totalBytesReceived = 0; bool success = false; socket.waitForReadyRead(1000); while (socket.bytesAvailable() > 0 || socket.waitForReadyRead(30000)) { QByteArray data = socket.readAll(); file.write(data); totalBytesReceived += data.size(); if (totalBytesReceived >= m_fileInfo.size) { break; } } file.close(); if (totalBytesReceived == m_fileInfo.size) { success = static_cast<ClientWidget*>(m_parent)->verifyFileHash(filePath, m_fileInfo.hash); } socket.disconnectFromHost(); emit static_cast<ClientWidget*>(m_parent)->downloadFinished(m_fileInfo.relativePath, success); } ClientWidget::ClientWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ClientWidget), tcpSocket(new QTcpSocket(this)), threadPool(new QThreadPool(this)), maxConcurrentDownloads(3), completedDownloads(0), totalFiles(0), isDownloading(false), isReceivingFileList(false), concurrentValidator(new QIntValidator(1, 512, this)) { setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint); ui->setupUi(this); threadPool->setMaxThreadCount(maxConcurrentDownloads); ui->lineEditConcurrent->setValidator(concurrentValidator); ui->lineEditConcurrent->setText(QString::number(maxConcurrentDownloads)); connect(ui->lineEditConcurrent, &QLineEdit::textChanged, this, &ClientWidget::onConcurrentTextChanged); loadConnectionSettings(); QString downloadDir = getParentDir(); ui->lineEdit_Dir_Clic->setText(downloadDir); connect(tcpSocket, &QTcpSocket::connected, this, &ClientWidget::onSocketConnected); connect(tcpSocket, &QTcpSocket::disconnected, this, &ClientWidget::onSocketDisconnected); connect(tcpSocket, &QTcpSocket::readyRead, this, &ClientWidget::onSocketReadyRead); connect(tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &ClientWidget::onSocketError); showLog("客户端初始化完成"); } ClientWidget::~ClientWidget() { if (tcpSocket && tcpSocket->state() == QAbstractSocket::ConnectedState) { tcpSocket->disconnectFromHost(); tcpSocket->waitForDisconnected(1000); } delete tcpSocket; delete ui; } void ClientWidget::onConcurrentTextChanged(const QString &text) { bool ok; int value = text.toInt(&ok); if (ok && value >= 1 && value <= 512) { maxConcurrentDownloads = value; threadPool->setMaxThreadCount(value); showLog("并发下载数已设置为: " + QString::number(value)); } else if (!text.isEmpty()) { ui->lineEditConcurrent->setText(QString::number(maxConcurrentDownloads)); } } QString ClientWidget::getParentDir() { QDir currentDir = QDir::current(); if (currentDir.cdUp()) { return currentDir.absolutePath(); } else { return currentDir.absolutePath(); } } void ClientWidget::showLog(const QString &message) { QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); ui->textBrowser->append("[" + timestamp + "] " + message); QTextCursor cursor = ui->textBrowser->textCursor(); cursor.movePosition(QTextCursor::End); ui->textBrowser->setTextCursor(cursor); } void ClientWidget::on_radioButton_clicked() { bool enable = ui->radioButton->isChecked(); ui->lineEdit_IP->setEnabled(enable); ui->lineEdit_Port->setEnabled(enable); } void ClientWidget::on_pushButton_clicked() { if (isDownloading) { showLog("停止下载..."); resetTransferState(); ui->pushButton->setText("下载"); ui->pushButton->setEnabled(true); ui->radioButton->setEnabled(true); isDownloading = false; return; } if(ui->pushButton->text() == "下载") { QString serverIP = ui->lineEdit_IP->text(); if (serverIP.isEmpty()) { QMessageBox::warning(this, "警告", "请输入服务器IP地址"); return; } bool portOk; quint16 port = ui->lineEdit_Port->text().toUShort(&portOk); if (!portOk || port == 0) { QMessageBox::warning(this, "警告", "请输入有效的端口号"); return; } saveConnectionSettings(); resetTransferState(); isDownloading = true; ui->pushButton->setText("停止"); ui->lineEdit_IP->setEnabled(false); ui->lineEdit_Port->setEnabled(false); ui->radioButton->setChecked(false); ui->radioButton->setEnabled(false); showLog("正在连接服务器: " + serverIP + ":" + QString::number(port)); connectToServer(); } } void ClientWidget::connectToServer() { QString serverIP = ui->lineEdit_IP->text(); quint16 port = ui->lineEdit_Port->text().toUShort(); tcpSocket->connectToHost(serverIP, port); if (!tcpSocket->waitForConnected(5000)) { showLog("连接超时: " + tcpSocket->errorString()); resetTransferState(); ui->pushButton->setText("下载"); ui->pushButton->setEnabled(true); ui->radioButton->setEnabled(true); isDownloading = false; return; } } void ClientWidget::onSocketConnected() { showLog("已连接到服务器 " + ui->lineEdit_IP->text() + ":" + ui->lineEdit_Port->text()); isReceivingFileList = true; receiveBuffer.clear(); showLog("正在获取文件列表..."); } void ClientWidget::onSocketDisconnected() { showLog("与服务器断开连接"); if (isDownloading && completedDownloads < totalFiles) { showLog("下载被中断"); } resetTransferState(); ui->pushButton->setText("下载"); ui->pushButton->setEnabled(true); ui->radioButton->setEnabled(true); isDownloading = false; } void ClientWidget::onSocketReadyRead() { QByteArray data = tcpSocket->readAll(); if (isReceivingFileList) { receiveBuffer.append(data); if (receiveBuffer.contains("FILE_LIST_END")) { processFileListData(receiveBuffer); isReceivingFileList = false; receiveBuffer.clear(); } } } void ClientWidget::onSocketError(QAbstractSocket::SocketError error) { Q_UNUSED(error); showLog("网络错误: " + tcpSocket->errorString()); resetTransferState(); ui->pushButton->setText("下载"); ui->pushButton->setEnabled(true); ui->radioButton->setEnabled(true); isDownloading = false; } void ClientWidget::processFileListData(const QByteArray& data) { fileList.clear(); QStringList lines = QString::fromUtf8(data).split('\n', Qt::SkipEmptyParts); bool parsingFiles = false; int fileCount = 0; for (const QString& line : lines) { if (line.startsWith("FILE_LIST_START:")) { parsingFiles = true; QString countStr = line.mid(16).split(':')[0]; totalFiles = countStr.toInt(); continue; } if (line == "FILE_LIST_END") { break; } if (parsingFiles && line.contains('|')) { QStringList parts = line.split('|'); if (parts.size() >= 3) { FileInfo fileInfo; fileInfo.relativePath = parts[0]; fileInfo.size = parts[1].toLongLong(); fileInfo.hash = QByteArray::fromHex(parts[2].toLatin1()); fileList.insert(fileInfo.relativePath, fileInfo); fileCount++; } } } showLog("获取到文件列表,共 " + QString::number(fileCount) + " 个文件"); if (fileCount > 0) { startDownloads(); } else { showLog("错误: 文件列表为空"); tcpSocket->disconnectFromHost(); } } void ClientWidget::startDownloads() { if (fileList.isEmpty()) { showLog("错误: 文件列表为空"); return; } completedDownloads = 0; totalFiles = fileList.size(); showLog("开始下载 " + QString::number(totalFiles) + " 个文件,并发数: " + QString::number(maxConcurrentDownloads)); for (auto it = fileList.constBegin(); it != fileList.constEnd(); ++it) { const FileInfo& fileInfo = it.value(); FileDownloadTask* task = new FileDownloadTask( ui->lineEdit_IP->text(), ui->lineEdit_Port->text().toUShort(), fileInfo, ui->lineEdit_Dir_Clic->text(), this ); connect(task, &FileDownloadTask::downloadFinished, this, &ClientWidget::onDownloadFinished, Qt::QueuedConnection); threadPool->start(task); } } void ClientWidget::onDownloadFinished(const QString& filePath, bool success) { completedDownloads++; if (success) { showLog("下载完成: " + filePath + " (" + QString::number(completedDownloads) + "/" + QString::number(totalFiles) + ")"); } else { showLog("下载失败: " + filePath); } if (completedDownloads >= totalFiles) { showLog("所有文件下载完成"); if (tcpSocket && tcpSocket->state() == QAbstractSocket::ConnectedState) { tcpSocket->write("ALL_FILES_COMPLETE\n"); tcpSocket->disconnectFromHost(); } ui->pushButton->setText("下载"); ui->pushButton->setEnabled(true); ui->radioButton->setEnabled(true); isDownloading = false; } } bool ClientWidget::verifyFileHash(const QString& filePath, const QByteArray& expectedHash) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { return false; } QCryptographicHash hash(QCryptographicHash::Md5); const qint64 bufferSize = 8192; char buffer[bufferSize]; qint64 bytesRead; while ((bytesRead = file.read(buffer, bufferSize)) > 0) { hash.addData(buffer, bytesRead); } file.close(); QByteArray actualHash = hash.result(); return (actualHash == expectedHash); } void ClientWidget::resetTransferState() { fileList.clear(); receiveBuffer.clear(); completedDownloads = 0; totalFiles = 0; isReceivingFileList = false; threadPool->clear(); threadPool->waitForDone(1000); } void ClientWidget::saveConnectionSettings() { QString encryptionKey = "Connect.set"; QString configPath = QDir::currentPath() + "/LocConfig"; QDir dir; if (!dir.exists(configPath)) { dir.mkpath(configPath); } QString filePath = configPath + "/Connset.con"; QSettings settings(filePath, QSettings::IniFormat); QString serverIP = ui->lineEdit_IP->text(); quint16 port = ui->lineEdit_Port->text().toUShort(); if (!serverIP.isEmpty() && port > 0) { QString encryptedIP = Encrypt(serverIP, encryptionKey); settings.setValue("Network/IPAddress", encryptedIP); settings.setValue("Network/Port", port); } } void ClientWidget::loadConnectionSettings() { QString encryptionKey = "Connect.set"; QString configPath = QDir::currentPath() + "/LocConfig"; QString filePath = configPath + "/Connset.con"; QFile file(filePath); if (file.exists()) { QSettings settings(filePath, QSettings::IniFormat); QString encryptedIP = settings.value("Network/IPAddress").toString(); quint16 port = settings.value("Network/Port", 15210).toUInt(); if (!encryptedIP.isEmpty()) { QString serverIP = Decrypt(encryptedIP, encryptionKey); ui->lineEdit_IP->setText(serverIP); } ui->lineEdit_Port->setText(QString::number(port)); } ui->radioButton->setEnabled(true); if (!ui->lineEdit_Port->text().isEmpty()) { ui->lineEdit_Port->setEnabled(false); } } [文件: clientwidget.ui] <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ClientWidget</class> <widget class="QWidget" name="ClientWidget"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>575</width> <height>400</height> </rect> </property> <property name="windowTitle"> <string>文件客户端</string> </property> <widget class="QLineEdit" name="lineEdit_Port"> <property name="geometry"> <rect> <x>370</x> <y>10</y> <width>61</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>15210</string> </property> <property name="maxLength"> <number>5</number> </property> </widget> <widget class="QLineEdit" name="lineEdit_IP"> <property name="enabled"> <bool>false</bool> </property> <property name="geometry"> <rect> <x>110</x> <y>10</y> <width>181</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>15</pointsize> </font> </property> <property name="maxLength"> <number>15</number> </property> </widget> <widget class="QTextBrowser" name="textBrowser"> <property name="geometry"> <rect> <x>5</x> <y>100</y> <width>470</width> <height>241</height> </rect> </property> </widget> <widget class="QLabel" name="label_2"> <property name="geometry"> <rect> <x>10</x> <y>10</y> <width>101</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>服务器IP:</string> </property> </widget> <widget class="QPushButton" name="pushButton"> <property name="geometry"> <rect> <x>480</x> <y>240</y> <width>91</width> <height>41</height> </rect> </property> <property name="font"> <font> <pointsize>20</pointsize> </font> </property> <property name="text"> <string>下载</string> </property> </widget> <widget class="QRadioButton" name="radioButton"> <property name="geometry"> <rect> <x>450</x> <y>10</y> <width>101</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>14</pointsize> </font> </property> <property name="text"> <string>修改端口</string> </property> <property name="checkable"> <bool>true</bool> </property> <property name="checked"> <bool>false</bool> </property> </widget> <widget class="QLineEdit" name="lineEdit_Dir_Clic"> <property name="enabled"> <bool>false</bool> </property> <property name="geometry"> <rect> <x>140</x> <y>60</y> <width>421</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>10</pointsize> </font> </property> </widget> <widget class="QLabel" name="label"> <property name="geometry"> <rect> <x>314</x> <y>10</y> <width>52</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>端口:</string> </property> </widget> <widget class="QLabel" name="label_4"> <property name="geometry"> <rect> <x>10</x> <y>60</y> <width>131</width> <height>31</height> </rect> </property> <property name="font"> <font> <family>新宋体</family> <pointsize>12</pointsize> </font> </property> <property name="text"> <string>客户端文件路径:</string> </property> </widget> <widget class="QLabel" name="label_concurrent"> <property name="geometry"> <rect> <x>480</x> <y>100</y> <width>71</width> <height>16</height> </rect> </property> <property name="text"> <string>并发下载数:</string> </property> </widget> <widget class="QLineEdit" name="lineEditConcurrent"> <property name="geometry"> <rect> <x>480</x> <y>120</y> <width>61</width> <height>22</height> </rect> </property> <property name="text"> <string>3</string> </property> <property name="maxLength"> <number>3</number> </property> </widget> </widget> <resources/> <connections/> </ui> ================================================================================ README.md - 使用说明文档 ================================================================================ [文件: README.md] # Qt文件传输系统 ## 功能特点 - 无XML依赖,直接目录同步 - 支持1-512个并发传输 - 文件MD5完整性校验 - 保持服务器目录结构 - 系统托盘支持 - 详细的传输日志 ## 编译说明 ### 服务器端 1. 使用Qt Creator打开WinServ.pro 2. 选择构建套件 3. 点击构建 ### 客户端 1. 使用Qt Creator打开WinClient.pro 2. 选择构建套件 3. 点击构建 ## 使用说明 ### 服务器端 1. 设置服务器文件目录 2. 设置监听端口(默认15210) 3. 设置并发传输数(1-512) 4. 点击"启动"开始服务 ### 客户端 1. 输入服务器IP和端口 2. 设置本地保存目录 3. 设置并发下载数(1-512) 4. 点击"下载"开始传输 ## 协议格式 ### 文件列表
最新发布
09-27
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值