Qt TCP网络编程————实现简单文字和文件传输

1 发送文字

1.1 服务端

1.1.1 添加模块

在pro中加入网络模块:

QT += network

1.1.2 头文件coding

添加文件头:

#include <QAbstractSocket>
#include <QTcpServer>

添加类前置声明:

class QTcpServer;

添加私有声明:

private slots://槽函数声明
	void sendMessage();//发送文字
private:
    QTcpServer tcpServer;

1.1.3 源文件(cpp)coding

添加头文件:

#include <QtNetwork>
#include <QDebug>

在模块的构造函数中添加:

//创建实例对象
tcpServer = new QTcpServer(this);
//监听端口
      if(!tcpServer->listen(QHostAddress::LocalHost, 6666)){//如果不是本地的6666端口,则报错,并关闭
        qDebug() << tcpServer->errorString();
        close();
    }
    //否则,则发射newConnection信号,连接
    connect(tcpServer, &QTcpServer::newConnection,
            this, &Server::sendMessage);

槽函数实现:

void Server::sendMessage()
{
    //暂存要发送的数据
    QByteArray block;
    QDataStream out (&block, QIODevice::WriteOnly);
    //设置数据流的版本(服务端要和客户端相同)
    out.setVersion(QDataStream::Qt_5_6);
    //预留两个字节的大小,便于后面填写数据的大小信息
    out << (quint16)0;
    out << tr("hello TCP!!!");//使用tr编码
    //跳转到数据块开头
    out.device()->seek(0);
    //获得实际数据大小,填充前面空的两个字节
    out << (quint64)(block.size() - sizeof (quint16));

    //获得已建立连接的套接字
    //获取连接套接字
    QTcpSocket* clientConnection = tcpServer->nextPendingConnection();
    //断开连接时,删除该套接字
        connect(clientConnection, &QTcpSocket::disconnected,
            clientConnection, &QTcpSocket::deleteLater);
    //发送block数据
    clientConnection->write(block);
    //一直等所有套接字发送完毕后,关闭套接字
    clientConnection->disconnectFromHost();

    //发送数据成功后,显示提醒
    ui->label->setText("send message successful!!!");

}

1. 2 客户端

1. 2.1 添加模块

在pro中加入网络模块:

QT += network

1. 2.2 头文件coding

添加头文件:

#include <QAbstractSocket>

添加类前置声明:

class QTcpSocket;

添加私有成员数据结构:

private:
    QTcpSocket* tcpSocket;
    QString message;
    quint16 blockSize; //用来存放数据的大小

添加成员函数:

private slots:
    void newConnect();
    void readMessage();
    void displayError(QAbstractSocket::SocketError);

    void on_pushButton_clicked();

1. 2.3 源文件(cpp)coding

添加头文件:

#include <QtNetwork>
#include <QAbstractSocket>
#include <QDebug>

在模块的构造函数中添加:

    tcpSocket = new QTcpSocket(this);
    //可读的
    connect(tcpSocket, SIGNAL(readyDead()), this, SLOT(readMessage()));
    //错误的
    connect(tcpSocket, SIGNAL(error(QAbstracSocket::SocketError)), this,
            SLOT(displayError(QAbstractSocket::SocketError)));

添加readMessage槽函数定义:

void Client::readMessage()
{
    QDataStream in(tcpSocket);
    //设置数据流版本,要和服务器一样
    in.setVersion(QDataStream::Qt_5_6);
    //刚开始接收数据
    if(blockSize == 0){
        //判断接收数据是否大于两个字节
        //也就是文件信息所占的字节
        if(tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return;
        in >> blockSize;
    }
    //没有得到全部数据,继续接收
    if(tcpSocket->bytesAvailable() < blockSize) return;
    //将接收到的数据存放在变量中
    in >> message;
    //显示接收到的数据
    ui->messageLabel->setText(message);
}

添加displayError槽函数定义:

void Client::displayError(QAbstractSocket::SocketError)
{
    qDebug() << tcpSocket->errorString();
}

2 传输文件

2.1 服务端

2.1.1 添加模块

添加模块
在pro中加入网络模块:

QT += network

2.1.2 头文件coding

头文件以及类的前置声明:

#include <QAbstractSocket>
#include <QTcpServer>

class QTcpSocket;
class QFile;

添加私有数据结构:

private:
    QTcpServer tcpServer;
    QTcpSocket* tcpServerConnection;新增

    qint64 totalBytes;//存放总大小信息
    qint64 bytesReceived;//已收到数据的大小
    qint64 fileNameSize;//文件名的大小信息
    QString fileName;//存放文件名
    QFile* localFile;//本地文件
    QByteArray inBlock;//数据缓冲区

添加槽函数声明:

private slots:
    void start();//开始监听
    void acceptConnect();//初始化
    void updateServerProgress();//更新QProgressBar
    void displayError(QAbstractSocket::SocketError socketError);//展示错误
    void on_startButton_clicked();//按钮点击事件

2.1.3 源文件coding

头文件声明:

#include <QtNetwork>
#include <QDebug>

构造函数添加

    connect(&tcpServer, SIGNAL(newConnection()),
            this, SLOT(acceptConnect()));

监听端口(定义在Button里):

// 开始监听按钮
void Server::on_startButton_clicked()
{
    start();
}

监听事件:

void Server::start()
{
    if (!tcpServer.listen(QHostAddress::LocalHost, 6666)) {
        qDebug() << tcpServer.errorString();
        close();
        return;
    }
    
    ui->startButton->setEnabled(false);//按钮不可被按下
    //清空之前的数据
    totalBytes = 0;
    bytesReceived = 0;
    fileNameSize = 0;
    ui->serverStatusLabel->setText(tr("监听"));
    ui->serverProgressBar->reset();//重设进度条
}

acceptConnect槽函数定义:

//初始化工作,每次接收完文件后,都要重新监听
void Server::acceptConnection()
{

    tcpServerConnection = tcpServer.nextPendingConnection();
    //更新进度条
    connect(tcpServerConnection, SIGNAL(readyRead()),
            this, SLOT(updateServerProgress()));
      //错误
    connect(tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(displayError(QAbstractSocket::SocketError)));
    ui->serverStatusLabel->setText(tr("接受连接"));
    // 关闭服务器,不再进行监听
    tcpServer.close();
}

updateServerProgress槽函数定义:

void Server::updateServerProgress()
{
    QDataStream in(tcpServerConnection);
    in.setVersion(QDataStream::Qt_4_0);

    // 如果接收到的数据小于16个字节,保存到来的文件头结构
    if (bytesReceived <= sizeof(qint64)*2) {
        if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2)
                && (fileNameSize == 0)) {
            // 接收数据总大小信息和文件名大小信息
            in >> totalBytes >> fileNameSize;
            bytesReceived += sizeof(qint64) * 2;
        }
        if((tcpServerConnection->bytesAvailable() >= fileNameSize)
                && (fileNameSize != 0)) {
            // 接收文件名,并建立文件
            in >> fileName;
            ui->serverStatusLabel->setText(tr("接收文件 %1 …")
                                           .arg(fileName));
            bytesReceived += fileNameSize;
            localFile = new QFile(fileName);
            if (!localFile->open(QFile::WriteOnly)) {
                qDebug() << "server: open file error!";
                return;
            }
        } else {
            return;
        }
    }
    // 如果接收的数据小于总数据,那么写入文件
    if (bytesReceived < totalBytes) {
        bytesReceived += tcpServerConnection->bytesAvailable();
        inBlock = tcpServerConnection->readAll();
        localFile->write(inBlock);
        inBlock.resize(0);
    }
    ui->serverProgressBar->setMaximum(totalBytes);
    ui->serverProgressBar->setValue(bytesReceived);

    // 接收数据完成时
    if (bytesReceived == totalBytes) {
        tcpServerConnection->close();
        localFile->close();
        ui->startButton->setEnabled(true);
        ui->serverStatusLabel->setText(tr("接收文件 %1 成功!")
                                       .arg(fileName));
    }
}

displayError槽函数定义:

void Server::displayError(QAbstractSocket::SocketError socketError)
{
    qDebug() << tcpServerConnection->errorString();
    tcpServerConnection->close();
    ui->serverProgressBar->reset();
    ui->serverStatusLabel->setText(tr("服务端就绪"));
    ui->startButton->setEnabled(true);
}

2.2 客户端

2.2.1 添加模块

添加模块
添加模块
在pro中加入网络模块:

QT += network

2.2.2 头文件coding

头文件和前置类声明:

#include <QDialog>
#include <QAbstractSocket>
#include <Qfile>

class QTcpSocket;
class QFile;

私有数据数据结构成员定义:

private:
    QTcpSocket* tcpClient;
    QFile* localFile;//要发送的数据
    qint64 totalBytes;//发送数据的大小
    qint64 bytesWriten;//已发送数据大小
    qint64 bytesToWrite;//剩余的数据大小
    qint64 payloadSize; //每次发送数据的大小
    QString fileName;//保存文件路径
    QByteArray outBlock;//数据缓冲区

槽函数声明:

private slots:
    void openFile();//打开文件
    void send();//连接服务器
    void startTransfer();//发送文件头结构(数据的大小、文件名大小、文件名)
    void updateClientProgress(quint64);//跟新进度条
    void displayError(QAbstractSocket::SocketError);//显示错误

    void on_openButton_clicked();//打开文件

    void on_sendButton_clicked();//发送文件

2.2.3 源文件coding

添加头文件:

#include <QtNetwork>
#include <QAbstractSocket>
#include <QDebug>

构造函数添加:

 //变量初始化
    payloadSize = 64 * 1024;//64kb
    totalBytes = 0;
    bytesWriten = 0;
    bytesToWrite = 0;

    tcpClient = new QTcpSocket(this);

    //服务器连接成功时,发出conenct()信号,开始传送文件
    connect(tcpClient, SIGNAL(connected()), this, SLOT(startTransfer()));
    //更新进度题条
    connect(tcpClient, SIGNAL(bytesWritten(qint64)), this,
            SLOT(updateClientProgress(qint64)));
    //展示错误
    connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(displayError(QAbstracSocket::SocketError)));

打开文件按钮点击事件:

void Client::on_openButton_clicked()
{
    ui->clientProgressBar->reset();
    ui->clientStatusLabel->setText(tr("状态:等待打开文件"));
    openFile();
}

openFile定义:

void Client::openFile()
{
    fileName = QFileDialog::getOpenFileName(this);
    if(!fileName.isEmpty()){
        ui->sendButton->setEnabled(true);//按键能不能被按下
        ui->clientStatusLabel->setText(tr("打开文件 %1 成功!").arg(fileName));
    }
}

发送文件按钮点击事件:

void Client::on_sendButton_clicked()
{
    send();
}

send函数定义:

void Client::send()
{
    ui->sendButton->setEnabled(false);
    //连接服务器
    tcpClient->connectToHost(ui->hostLineEdit->text(),
                             ui->portLineEdit->text().toInt());

}

startTransfer槽函数定义:

void Client::startTransfer()
{
    localFile  = new QFile(fileName);
    if(!localFile->open(QFile::ReadOnly)){
        qDebug() << "client: open file error";
        return;
    }
    //获得文件大小
    totalBytes  = localFile->size();

    QDataStream sendOut(&outBlock, QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_6);
    QString currentFileName = fileName.right(fileName.size()
                                             - fileName.lastIndexOf('/') - 1);
    //保留总大小信息,文件名大小信息,然后输入文件名
    //总大小:总大小信息,文件名大小信息,文件名和实际文件大小总和
    sendOut << qint64(0) << quint64(0) << currentFileName;
    //整个数据的大小 = 文件头结构 + 实际文件大小
    //整个数据的大小放在数据流最开始,占用第一个qint64(0)
    //文件名大小占用第二个qint64(0)
    totalBytes += outBlock.size();
    sendOut.device()->seek(0);

    //返回outblock信息
    //用两个实际的大小代替两个qint64(0)空间
    sendOut << totalBytes << qint64(outBlock.size() - sizeof (qint64)*2);
    //发送完文件头结构后,剩余数据大小
    bytesToWrite = totalBytes - tcpClient->write(outBlock);
    ui->clientStatusLabel->setText(tr("已连接"));

    outBlock.resize(0);


}

updateClientProgress槽函数定义:

void Client::updateClientProgress(quint64 numBytes)
{
    //已发送数据的大小
    bytesWriten += (int) numBytes;
    //如果已经发送了数据
    if(bytesToWrite > 0){
        //每次发送payloadSize大小的数据,也就是64k
        //如果不足64k, 就发送剩余的数据大小
        outBlock = localFile->read(qMin(bytesToWrite, payloadSize));

        bytesToWrite -= (int)tcpClient->write(outBlock);
        //清空发送缓存
        outBlock.resize(0);
    }else{//没有任何发送的数据,则关闭文件
        localFile->close();
    }
    //更新进度条
    ui->clientProgressBar->setMaximum(totalBytes);
    ui->clientProgressBar->setValue(bytesWriten);
    //如果发送完毕
    if(bytesWriten == totalBytes){
        ui->clientStatusLabel->setText(tr("传送文件 %1 成功").arg(fileName));
        localFile->close();
        tcpClient->close();
    }
}

displayError槽函数定义:

void Client::displayError(QAbstractSocket::SocketError)
{
    qDebug() << tcpClient->errorString();
    tcpClient->close();
    ui->clientProgressBar->reset();
    ui->clientStatusLabel->setText(tr("客户端就绪"));
    ui->sendButton->setEnabled(true);
}

继续学习Qt TCP编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星蓝雨

如果觉得文章不错,可以请喝咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值