这个TFTP自己写的服务器已经可以正常使用了,不想用外挂的tftp64软件的朋友们可以试试我这个,完整代码都在。

本文介绍了如何使用C++和Qt库实现一个简单的TFTP服务器,包括处理不同操作码(如RRQ、WRQ、DATA、ACK和ERROR),文件读写以及错误处理。主要展示了`MainWindow`类中的关键函数和数据结构的使用。
摘要由CSDN通过智能技术生成
https://blog.csdn.net/soldier_d/article/details/115311866#ifndef MAINWINDOW_H
这个链接是别人写的客户端  因为没人人写服务端 所以我就自己写了一个,跟这个联合起来用也可以,也可以把我这个服务端进行修改改成客户端。做这个TFTP服务端的原因是我想全部编辑在exe里面而不是要挂靠软件在单独去开,非常方便的解决很多网络服务上传下载文件的问题。
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QUdpSocket>
#include <QFile>
#include <QMap>
#include <QTimer>
#include <QCoreApplication>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
 
 
// 定义TFTP操作码
enum Opcode {
    RRQ = 1,   // Read request
    WRQ = 2,   // Write request
    DATA = 3,  // Data
    ACK = 4,   // Acknowledgment
    ERROR = 5  // Error
};
 
// TFTP错误码
enum ErrorCode {
    FileNotFound = 1,
    AccessViolation = 2,
    DiskFull = 3,
    IllegalOperation = 4,
    UnknownTransferID = 5,
    FileAlreadyExists = 6,
    NoSuchUser = 7
};
 
// 服务器配置
const quint16 TftpPort = 69; // TFTP标准端口
const int BlockSize = 512; // 数据块大小
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
     void start();
private slots:
    void processPendingDatagrams();
    void on_pushButton_clicked();
 
private:
    Ui::MainWindow *ui;
    QUdpSocket socket;
        QMap<quint16, QString> sessions; // 会话映射,用于跟踪每个传输的状态
 
        void handleRRQ(const QHostAddress& clientAddr, quint16 clientPort, const QString& fileName);
        void handleWRQ(const QHostAddress& clientAddr, quint16 clientPort, const QString& fileName);
        void handleData(const QHostAddress& clientAddr, quint16 clientPort, quint16 blockNumber, const QByteArray& data);
        void handleAck(const QHostAddress& clientAddr, quint16 clientPort, quint16 blockNumber);
        void handleError(const QHostAddress& clientAddr, quint16 clientPort, ErrorCode errorCode, const QString& errorMessage);
 
        void sendAck(const QHostAddress& clientAddr, quint16 clientPort, quint16 blockNumber);
        void sendData(const QHostAddress& clientAddr, quint16 clientPort, quint16 blockNumber, const QByteArray& data);
        void sendError(const QHostAddress& clientAddr, quint16 clientPort, ErrorCode errorCode, const QString& errorMessage);
 
        QByteArray createAckPacket(quint16 blockNumber);
        QByteArray createDataPacket(quint16 blockNumber, const QByteArray& data);
        QByteArray createErrorPacket(ErrorCode errorCode, const QString& errorMessage);
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtEndian>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(&socket, &QUdpSocket::readyRead, this, &MainWindow::processPendingDatagrams);
 
       // 也可以在这里启动服务器,或者根据需要放在其他地方
    ui->lineEdit->setPlaceholderText("请输入当前计算机的ip地址");
 
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::start()
{
    QHostAddress address(ui->lineEdit->text());
    if (!socket.bind(address,TftpPort)) {
         qDebug() << "Could not start TFTP server on port" << TftpPort;
         return;
     }
    qDebug() << "TFTP server started on port" << TftpPort;
}
 
void MainWindow::processPendingDatagrams()
{
    while (socket.hasPendingDatagrams()) {
           QByteArray datagram;
           QHostAddress sender;
           quint16 senderPort;
 
           datagram.resize(socket.pendingDatagramSize());
           socket.readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
 
           if (datagram.size() < 4) { // 最小长度检查
               qDebug() << "Received datagram is too short.";
               continue;
           }
 
           quint16 opcode = qFromBigEndian<quint16>((uchar*)datagram.constData());
            qDebug() << "opcode" << opcode;
           switch (opcode) {
               case RRQ: {
                   // 提取文件名和模式
                   QStringList parts = QString::fromUtf8(datagram.mid(2)).split(QChar(0), QString::SkipEmptyParts);
                   if (parts.size() >= 1) {
                       QString fileName = parts[0];
                       handleRRQ(sender, senderPort, fileName);
                   }
                   break;
               }
               case WRQ: {
                   QStringList parts = QString::fromUtf8(datagram.mid(2)).split(QChar(0), QString::SkipEmptyParts);
                   if (parts.size() >= 1) {
                       QString fileName = parts[0];
                       handleWRQ(sender, senderPort, fileName);
                   }
                   break;
               }
               case DATA: {
                   if (datagram.size() >= 4) {
                       quint16 blockNumber = qFromBigEndian<quint16>((uchar*)datagram.constData() + 2);
                       QByteArray data = datagram.mid(4);
                       handleData(sender, senderPort, blockNumber, data);
                   }
                   break;
               }
               case ACK: {
                   if (datagram.size() >= 4) {
                       quint16 blockNumber = qFromBigEndian<quint16>((uchar*)datagram.constData() + 2);
                       handleAck(sender, senderPort, blockNumber);
                   }
                   break;
               }
               case ERROR: {
                   if (datagram.size() >= 4) {
                       quint16 errorCode = qFromBigEndian<quint16>((uchar*)datagram.constData() + 2);
//                       QString errorMessage = QString::fromUtf8(datagram.mid(4).split(QChar(0))[0]);
                       QString errorMessage = QString::fromUtf8(datagram.mid(4)).split(QChar(0))[0];
 
                       handleError(sender, senderPort, static_cast<ErrorCode>(errorCode), errorMessage);
                   }
                   break;
               }
               default:
                   qDebug() << "Unknown or unsupported opcode" << opcode;
                   break;
           }
 
    }
}
 
void MainWindow::handleRRQ(const QHostAddress &clientAddr, quint16 clientPort, const QString &fileName)
{
    QFile file(fileName);
       if (!file.open(QIODevice::ReadOnly)) {
           qDebug() << "Failed to open file" << fileName;
           sendError(clientAddr, clientPort, FileNotFound, "File not found");
           return;
       }
 
       quint16 blockNumber = 1;
       QByteArray data = file.read(BlockSize);
       while (!data.isEmpty()) {
           sendData(clientAddr, clientPort, blockNumber++, data);
           if (data.size() < BlockSize) {
               break; // Last block
           }
           data = file.read(BlockSize);
       }
}
 
void MainWindow::handleWRQ(const QHostAddress &clientAddr, quint16 clientPort, const QString &fileName)
{
//    QFile *file = new QFile(fileName);
//        if (!file->open(QIODevice::WriteOnly)) {
//            qDebug() << "Failed to create file" << fileName;
//            sendError(clientAddr, clientPort, AccessViolation, "Cannot create file");
//            delete file;
//            return;
//        }
 
//        // 保存文件对象以便后续写操作
//        sessions.insert(clientPort, fileName);
 
//        // 发送ACK 0来启动传输
//        sendAck(clientAddr, clientPort, 0);
 
 
 
//        QString filePath = "/usr/data/DataPrograms/" + fileName;
        QFile *file = new QFile(fileName);
        if (!file->open(QIODevice::WriteOnly)) {
            qDebug() << "Failed to create file" << fileName;
            sendError(clientAddr, clientPort, AccessViolation, "Cannot create file");
            delete file;
            return;
        }
 
        sessions.insert(clientPort, fileName);
        qDebug() << "File" << fileName << "opened for writing.";
 
        sendAck(clientAddr, clientPort, 0);
}
 
void MainWindow::handleData(const QHostAddress &clientAddr, quint16 clientPort, quint16 blockNumber, const QByteArray &data)
{
    // 查找对应的文件和会话
    if (!sessions.contains(clientPort)) {
        sendError(clientAddr, clientPort, UnknownTransferID, "Unknown transfer ID");
        return;
    }
 
    QString fileName = sessions.value(clientPort);
    QFile file(fileName);
    if (!file.open(QIODevice::Append)) {
        qDebug() << "Failed to open file" << fileName;
        sendError(clientAddr, clientPort, AccessViolation, "Cannot write to file");
        return;
    }
 
    file.write(data);  // 数据包的前4个字节是操作码和块编号    之前这里做了mid(4),为的是删除 之后发现不需要,所以改了。
    file.close();
 
    // 发送ACK确认
    sendAck(clientAddr, clientPort, blockNumber);
}
 
void MainWindow::handleAck(const QHostAddress &clientAddr, quint16 clientPort, quint16 blockNumber)
{
    // 在这里处理ACK,通常是在发送文件时使用
       qDebug() << "ACK received for block" << blockNumber << "from" << clientAddr.toString() << ":" << clientPort;
 
       // 例如,可以在这里继续发送下一个数据块,如果有的话
}
 
void MainWindow::handleError(const QHostAddress &clientAddr, quint16 clientPort, ErrorCode errorCode, const QString &errorMessage)
{
    // 打印或处理错误
       qDebug() << "Error from" << clientAddr.toString() << ":" << clientPort << "," << errorCode << "," << errorMessage;
}
 
void MainWindow::sendAck(const QHostAddress &clientAddr, quint16 clientPort, quint16 blockNumber)
{
    QByteArray ackPacket = createAckPacket(blockNumber);
     socket.writeDatagram(ackPacket, clientAddr, clientPort);
}
 
void MainWindow::sendData(const QHostAddress &clientAddr, quint16 clientPort, quint16 blockNumber, const QByteArray &data)
{
    QByteArray packet = createDataPacket(blockNumber, data);
    socket.writeDatagram(packet, clientAddr, clientPort);
}
 
void MainWindow::sendError(const QHostAddress &clientAddr, quint16 clientPort, ErrorCode errorCode, const QString &errorMessage)
{
    QByteArray errorPacket = createErrorPacket(errorCode, errorMessage);
    socket.writeDatagram(errorPacket, clientAddr, clientPort);
}
 
QByteArray MainWindow::createAckPacket(quint16 blockNumber)
{
    QByteArray packet;
    QDataStream stream(&packet, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);
    stream << quint16(ACK) << blockNumber;
    return packet;
}
 
QByteArray MainWindow::createDataPacket(quint16 blockNumber, const QByteArray &data)
{
    QByteArray packet;
    QDataStream stream(&packet, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);
 
    // 操作码为DATA (通常为3)
    stream << quint16(DATA1) << blockNumber;
    packet.append(data);
 
    return packet;
}
 
QByteArray MainWindow::createErrorPacket(ErrorCode errorCode, const QString &errorMessage)
{
    QByteArray packet;
    QDataStream stream(&packet, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);
    stream << quint16(ERROR) << quint16(errorCode);
    packet.append(errorMessage.toUtf8());
    packet.append(char(0)); // 以0字节结束
    return packet;
}
 
 
void MainWindow::on_pushButton_clicked()
{
    start();
}

                
  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值