QT学习点滴记录(五)

QT 网络 TCP(二) 文件传输

一、服务端

1. 新建一个QT Gui Application, 命名为“TCPReceive”, 并修改pro文件(QT += network)
2. 添加所需控件, 如下图所示

其中“服务器端”Label的objectName为ServerStatusLabel;进度条Progress Bar的objectName为ServerProgressBar,设置其value属性为0;“开始监听”按钮的objectName为ListenPushButton
3. 修改Widget.h文件
(1) 添加头文件:#include <QtNetWork>
(2)添加私有变量:
Ui::Widget *ui;
QTcpServer m_TcpServer;
QTcpSocket* m_pTcpServerConnection;
qint64 m_nTotalBytes;//存放总大小信息
qint64 m_nBytesReceived;//已收到数据的大小
qint64 m_nFileNameSize;//文件名的大小信息
QString m_strFileName;//存放文件名
QFile* m_pLocalFile;//本地文件
QByteArray m_inBlock;//数据缓冲区
(3)添加槽函数
private slots:
    void Listen();
    void AcceptConnection();
    void UpdateServerProgress();
    void DiaplayError(QAbstractSocket::SocketError);
    void on_ListenPushButton_clicked();
4.修改Widget.cpp文件
(1)在构造函数添加如下代码:
m_nTotalBytes = 0;
m_nBytesReceived = 0;
m_nFileNameSize = 0;
connect(&m_TcpServer, SIGNAL(newConnection()), this,SLOT(AcceptConnection()));
(2)实现槽函数
void Widget::Listen()
{
    ui->ListenPushButton->setEnabled(false);
    m_nBytesReceived = 0;
    if(!m_TcpServer.listen(QHostAddress::LocalHost, 6666))
    {
        qDebug() << m_TcpServer.errorString();
        close();
        return ;
    }
    ui->ServerStatusLabel->setText(tr("监听"));
}
void Widget::AcceptConnection()
{
    m_pTcpServerConnection = m_TcpServer.nextPendingConnection();
    connect(m_pTcpServerConnection, SIGNAL(readyRead()), this,
            SLOT(UpdateServerProgress()));
    connect(m_pTcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(DiaplayError(QAbstractSocket::SocketError)));
    ui->ServerStatusLabel->setText(tr("接受连接"));
    m_TcpServer.close();
}
void Widget::UpdateServerProgress()
{
    QDataStream in(m_pTcpServerConnection);
    in.setVersion(QDataStream::Qt_4_8);
    if(m_nBytesReceived <= sizeof(qint64) * 2)
    {
        if(m_pTcpServerConnection->bytesAvailable() >= sizeof(qint64) * 2 && m_nFileNameSize == 0)
        {
            in >> m_nTotalBytes >> m_nFileNameSize;
            m_nBytesReceived += sizeof(qint64) * 2;
        }
        if(m_pTcpServerConnection->bytesAvailable() >= m_nFileNameSize && m_nFileNameSize != 0)
        {
            in >> m_strFileName;
            ui->ServerStatusLabel->setText(tr("接收文件 %1 ...").arg(m_strFileName));
            m_nBytesReceived += m_nFileNameSize;
            m_pLocalFile = new QFile(m_strFileName);
            if(!m_pLocalFile->open(QFile::WriteOnly))
            {
                qDebug() << "Open File Error!";
                return ;
            }
        }
        else
        {
            return ;
        }
    }
    if(m_nBytesReceived < m_nTotalBytes)
    {
        m_nBytesReceived += m_pTcpServerConnection->bytesAvailable();
        m_inBlock = m_pTcpServerConnection->readAll();
        m_pLocalFile->write(m_inBlock);
        m_inBlock.resize(0);
    }
    ui->ServerProgressBar->setMaximum(m_nTotalBytes);
    ui->ServerProgressBar->setValue(m_nBytesReceived);
    if(m_nBytesReceived == m_nTotalBytes)
    {
        m_pTcpServerConnection->close();
        m_pLocalFile->close();
        ui->ListenPushButton->setEnabled(true);
        ui->ServerStatusLabel->setText(tr("接收文件 %1 成功! ").arg(m_strFileName));
    }
}
void Widget::DiaplayError(QAbstractSocket::SocketError)
{
    qDebug() << m_pTcpServerConnection->errorString();
    m_pTcpServerConnection->close();
    ui->ServerProgressBar->reset();
    ui->ServerStatusLabel->setText(tr("服务端就绪"));
    ui->ListenPushButton->setEnabled(true);
}
void Widget::on_ListenPushButton_clicked()
{
    Listen();
}

5. 为了可以显示中文信息, 在main.cpp中添加如下代码:
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
6.编译运行, 效果如下图



二、客户端

1. 新建QT Gui Application程序, 命名为TCPSender, 并修改pro文件(添加代码:QT += network)
2. 在ui文件中添加所需控件, 示意图如下:

其中“主机”的Line Edit的objectName为HostLineEdit;“端口”后的Line Edit的objectName为PortLineEdit;下面的Progress Bar的objectName为ClientProgressBar,其value属性设为0;“状态”Label的objetName为ClientStatusLabel;“打开”按钮的objectName为openButton;“发送”按钮的objectName为SendPushButton
3. 修改Widget.h文件
(1)添加头文件:#include <QtNetWork>
(2)添加私有变量:
QTcpSocket* m_pTcpClient;  
QFile* m_pLocalFile;//要发送的文件
qint64 m_nTotalBytes;//数据总大小
qint64 m_nBytesWritten;//已经发送数据大小
qint64 m_nBytesToWrite;//剩余数据大小
qint64 m_nLoadSize;//每次发送数据的大小
QString m_strFileName;//保存文件路径
QByteArray m_OutBlock;//数据缓冲区, 即存放每次要发送的数据
(3)添加槽函数:
private slots:
    void Send();    //连接服务器
    void StartTransfer();   //发送文件大小等信息
    void UpdateClientProgress(qint64);  //发送数据, 更新进度条
    void DiaplayError(QAbstractSocket::SocketError);    //显示错误
    void OpenFile();    //打开文件
    void on_OpenPushButton_clicked();
    void on_SendPushButton_clicked();
4.修改Widget.cpp文件
(1)在构造函数中添加如下代码:
m_nLoadSize = 4 * 1024;
m_nTotalBytes = 0;
m_nBytesWritten = 0;
m_nBytesToWrite = 0;
m_pTcpClient = new QTcpSocket(this);
//当连接服务器成功时, 发出connected信号, 开始传送文件
connect(m_pTcpClient, SIGNAL(connected()), this,SLOT(StartTransfer()));
//当有数据发送成功时, 更新进度条
connect(m_pTcpClient, SIGNAL(bytesWritten(qint64)), this,SLOT(UpdateClientProgress(qint64)));
connect(m_pTcpClient, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(DiaplayError(QAbstractSocket::SocketError)));
//设置“发送”按钮不可用
ui->SendPushButton->setEnabled(false);
(2)实现槽函数
void Widget::OpenFile()
{
    m_strFileName = QFileDialog::getOpenFileName(this);
    if(!m_strFileName.isEmpty())
    {
        ui->SendPushButton->setEnabled(true);
        ui->ClientStatusLabel->setText(tr("打开文件 %1 成功!").arg(m_strFileName));
    }
}
void Widget::Send()
{
    //设置发送按钮不可用
    ui->SendPushButton->setEnabled(false);
    //初始化已发送字节为0
    m_nBytesWritten = 0;
    ui->ClientStatusLabel->setText(tr("连接中..."));
    m_pTcpClient->connectToHost(ui->HostLineEdit->text(), ui->PortLineEdit->text().toInt());
}
void Widget::StartTransfer()
{
    m_pLocalFile = new QFile(m_strFileName);
    if(!m_pLocalFile->open(QFile::ReadOnly))
    {
        qDebug() << "Open File Error!";
        return ;
    }
    m_nTotalBytes = m_pLocalFile->size();
    QDataStream sendOut(&m_OutBlock, QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_4_8);
    QString currentFileName = m_strFileName.right(m_strFileName.size() - m_strFileName.lastIndexOf('/') - 1);
    //依次写入总大小信息空间, 文件名大小信息空间, 文件名
    sendOut << qint64(0) << qint64(0) << currentFileName;
    //这里的总大小是文件名大小等信息和实际文件大小的总和
    m_nTotalBytes += m_OutBlock.size();
    sendOut.device()->seek(0);
    //返回m_OutBlock的开始, 用实际的大小信息代替两个qint64(0)空间
    sendOut << m_nTotalBytes << qint64(m_OutBlock.size() - sizeof(qint64) * 2);
    //发送完头数据后剩余数据的大小
    m_nBytesToWrite = m_nTotalBytes - m_pTcpClient->write(m_OutBlock);
    ui->ClientStatusLabel->setText(tr("已连接"));
    m_OutBlock.resize(0);
}
void Widget::UpdateClientProgress(qint64 numBytes)
{
    //已经发送数据的大小
    m_nBytesWritten += (int)numBytes;
    //如果已经发送了数据
    if(m_nBytesToWrite > 0)
    {
        //每次发送m_nLoadSize大小的数据, 这里设置为4KB, 如果剩余的数据不足, 就发送剩余数据的大小
        m_OutBlock = m_pLocalFile->read(qMin(m_nBytesToWrite, m_nLoadSize));
        //发送完一次数据后剩余数据的大小
        m_nBytesToWrite -= (int)m_pTcpClient->write(m_OutBlock);
        //清空发送缓冲区
        m_OutBlock.resize(0);
    }
    else    //如果没有发送任何数据, 则关闭文件
    {
        m_pLocalFile->close();
    }
    //更新进度条
    ui->ClientProgressBar->setMaximum(m_nTotalBytes);
    ui->ClientProgressBar->setValue(m_nBytesWritten);
    //发送完毕
    if(m_nBytesWritten == m_nTotalBytes)
    {
        ui->ClientStatusLabel->setText(tr("传送文件 %1 成功").arg(m_strFileName));
        m_pLocalFile->close();
        m_pTcpClient->close();
    }
}
void Widget::DiaplayError(QAbstractSocket::SocketError)
{
    qDebug() << m_pTcpClient->errorString();
    m_pTcpClient->close();
    ui->ClientProgressBar->reset();
    ui->ClientStatusLabel->setText(tr("客户端准备就绪"));
    ui->SendPushButton->setEnabled(true);
}
void Widget::on_OpenPushButton_clicked()
{
    OpenFile();
}
void Widget::on_SendPushButton_clicked()
{
    Send();
}
5.为了可以显示中文信息, 在main.cpp中添加如下代码:
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
6. 编译运行, 效果图如下:


7. 运行服务端, 然后点击监听按钮, 点击客户端中打开按钮, 选择本地文件, 进行传输, 示意图如下




































评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值