您的位置:首页 > 理论基础 > 计算机网络

qt4 tcp QTcpSocket QTcpServer 传输文件

2014-03-11 15:55 681 查看
tcpSender (客户端无QTcpServer)

[cpp] view
plaincopy

private:  

        QTcpSocket *tcpClient;  

        QFile *localFile;  //要发送的文件  

        qint64 totalBytes;  //数据总大小  

        qint64 bytesWritten;  //已经发送数据大小  

        qint64 bytesToWrite;   //剩余数据大小  

        qint64 loadSize;   //每次发送数据的大小  

        QString fileName;  //保存文件路径  

        QByteArray outBlock;  //数据缓冲区,即存放每次要发送的数据  

private slots:  

    void send();  //连接服务器  

    void startTransfer();  //发送文件大小等信息  

    void updateClientProgress(qint64); //发送数据,更新进度条  

    void displayError(QAbstractSocket::SocketError); //显示错误  

    void openFile();  //打开文件  

[cpp] view
plaincopy

Widget::Widget(QWidget *parent) :  

    QWidget(parent),  

    ui(new Ui::Widget)  

{  

    ui->setupUi(this);  

    loadSize = 4*1024;  

    totalBytes = 0;  

    bytesWritten = 0;  

    bytesToWrite = 0;  

    tcpClient = new QTcpSocket(this);  

    connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));  

    //当连接服务器成功时,发出connected()信号,我们开始传送文件  

    connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));  

    //当有数据发送成功时,我们更新进度条  

    connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));  

    ui->sendButton->setEnabled(false);  

    ui->hostLineEdit->setText("192.168.1.100");  

    ui->portLineEdit->setText("6666");  

}  

[cpp] view
plaincopy

void Widget::openFile()   //打开文件  

{  

    fileName = QFileDialog::getOpenFileName(this);  

    if(!fileName.isEmpty())  

    {  

        ui->sendButton->setEnabled(true);  

        ui->clientStatusLabel->setText(tr("打开文件 %1 成功!").arg(fileName));  

    }  

}  

  

  

void Widget::send()   //连接到服务器,执行发送  

{  

    ui->sendButton->setEnabled(false);  

    bytesWritten = 0;  

    //初始化已发送字节为0  

    ui->clientStatusLabel->setText(tr("连接中…"));  

    tcpClient->connectToHost(QHostAddress(ui->hostLineEdit->text()),ui->portLineEdit->text().toInt());//连接  

}  

  

void Widget::on_openButton_clicked() //打开按钮  

{  

    openFile();  

}  

  

void Widget::on_sendButton_clicked() //发送按钮  

{  

    send();  

}  

[cpp] view
plaincopy

void Widget::startTransfer()  //实现文件大小等信息的发送  

{  

    localFile = new QFile(fileName);  

    if(!localFile->open(QFile::ReadOnly))  

    {  

        qDebug() << "open file error!";  

        return;  

    }  

    totalBytes = localFile->size();  

    //文件总大小  

    QDataStream sendOut(&outBlock,QIODevice::WriteOnly);  

    sendOut.setVersion(QDataStream::Qt_4_6);  

    QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);  

    sendOut << qint64(0) << qint64(0) << currentFileName;  

    //依次写入总大小信息空间,文件名大小信息空间,文件名---仅是文件名不含路径,没必要含路径  

    totalBytes += outBlock.size();  

    //这里的总大小是文件名大小等信息和实际文件大小的总和  

    sendOut.device()->seek(0);  

    sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2));  

    //totalBytes是文件总大小,即两个quint64的大小+文件名+文件实际内容的大小  

    //qint64((outBlock.size() - sizeof(qint64)*2))得到的是文件名大小  

    bytesToWrite = totalBytes - tcpClient->write(outBlock);  

    //发送完头数据后剩余数据的大小,即文件实际内容的大小  

    ui->clientStatusLabel->setText(tr("已连接"));  

    outBlock.resize(0);  

}  

[cpp] view
plaincopy

void Widget::updateClientProgress(qint64 numBytes) //更新进度条,实现文件的传送  

{  

    bytesWritten += (int)numBytes;  

    //已经发送数据的大小  

    if(bytesToWrite > 0) //如果已经发送了数据  

    {  

        outBlock = localFile->read(qMin(bytesToWrite,loadSize));  

      //每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB,  

      //就发送剩余数据的大小  

        bytesToWrite -= (int)tcpClient->write(outBlock);  

       //发送完一次数据后还剩余数据的大小  

        outBlock.resize(0);  

       //清空发送缓冲区  

    }  

    else  

    {  

        localFile->close(); //如果没有发送任何数据,则关闭文件  

    }  

    ui->clientProgressBar->setMaximum(totalBytes);  

    ui->clientProgressBar->setValue(bytesWritten);  

    //更新进度条  

    if(bytesWritten == totalBytes) //发送完毕  

    {  

        ui->clientStatusLabel->setText(tr("传送文件 %1 成功").arg(fileName));  

        localFile->close();  

        tcpClient->close();  

    }  

}  

[cpp] view
plaincopy

void Widget::displayError(QAbstractSocket::SocketError) //显示错误  

{  

    qDebug() << tcpClient->errorString();  

    tcpClient->close();  

    ui->clientProgressBar->reset();  

    ui->clientStatusLabel->setText(tr("客户端就绪"));  

    ui->sendButton->setEnabled(true);  

}  

流程:
1创建tcpSocket ,tcpClient= new QTcpSocket(this);
2.关联信号connected和槽函数startTransfer,connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));其中信号connected在连接服务器成功(即本客户端执行 tcpClient->connectToHost,得到服务器的成功响应)时发射,此时执行startTransfer开始传输文件头数据
3.关联信号bytesWritten和槽函数updateClientProgress,connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));其中信号bytesWritten在当有数据发送成功时(即本客户端执行 tcpClient->write(outBlock);服务器的成功收outBlock时)时发射,此时执行updateClientProgress来更新进度条并接着发送剩余数据
4.连接到某个ip的某个端口,tcpClient->connectToHost(QHostAddress(ui->hostLineEdit->text()),ui->portLineEdit->text().toInt())
5.实现槽函数startTransfer,开始传输文件头数据
5.实现槽函数updateClientProgress,更新进度条,并接着发送剩余数据
其中发送的文件头包括一个quint64(记录文件总大小,包括sizeof(quint64)+sizeof(quint64)+文件名+文件实际内容),一个quint64(记录文件名大小),一个文件名不含路径,一个文件实际内容

tcpReceiver

[cpp] view
plaincopy

private:  

        QTcpServer tcpServer;  

        QTcpSocket *tcpServerConnection;  

        qint64 totalBytes;  //存放总大小信息  

        qint64 bytesReceived;  //已收到数据的大小  

        qint64 fileNameSize;  //文件名的大小信息  

        QString fileName;   //存放文件名  

        QFile *localFile;   //本地文件  

        QByteArray inBlock;   //数据缓冲区  

private slots:  

    void start();   //开始监听  

    void acceptConnection();  //建立连接  

    void updateServerProgress();  //更新进度条,接收数据  

    void displayError(QAbstractSocket::SocketError socketError);  

[cpp] view
plaincopy

Widget::Widget(QWidget *parent) :  

    QWidget(parent),  

    ui(new Ui::Widget)  

{  

    ui->setupUi(this);  

        totalBytes = 0;  

        bytesReceived = 0;  

        fileNameSize = 0;  

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

    //当发现新连接时发出newConnection()信号  

}  

[cpp] view
plaincopy

void Widget::start() //开始监听  

{  

    ui->startButton->setEnabled(false);  

    bytesReceived =0;  

    if(!tcpServer.listen(QHostAddress("192.168.1.100"),6666))  

    {  

        qDebug() << tcpServer.errorString();  

        close();  

        return;  

    }  

    ui->serverStatusLabel->setText(tr("监听"));  

}  

  

void Widget::on_startButton_clicked() //开始监听按钮  

{  

    start();  

}  

[cpp] view
plaincopy

void Widget::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();  

}  

[cpp] view
plaincopy

void Widget::updateServerProgress()  //更新进度条,接收数据  

{  

    QDataStream in(tcpServerConnection);  

    in.setVersion(QDataStream::Qt_4_6);  

  

    if(bytesReceived <= sizeof(qint64)*2)  

    { //如果接收到的数据小于16个字节,那么是刚开始接收数据,我们保存到//来的头文件信息  

  

        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() << "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));  

    }  

}  

[cpp] view
plaincopy

void Widget::displayError(QAbstractSocket::SocketError) //错误处理  

{  

    qDebug() << tcpServerConnection->errorString();  

    tcpServerConnection->close();  

    ui->serverProgressBar->reset();  

    ui->serverStatusLabel->setText(tr("服务端就绪"));  

    ui->startButton->setEnabled(true);  

}  

流程
1.创建 QTcpServer tcpServer;使之监听本机的某个端口,tcpServer.listen(QHostAddress("192.168.1.100"),6666)
2,关联信号newConnection和槽函数sendMessage, connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));其中信号newConnection在有客户端的连接请求(即客户端执行tcpSocket->connectToHost)时发射,此时执行acceptConnection,
3.实现槽函数acceptConnection,在里面
从tcpServer取得已经建立但挂起的QTcpSocket连接
tcpServerConnection = tcpServer.nextPendingConnection();
并关联信号readyRead和槽函数updateServerProgress,
connect(tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));其中信号readyRead在有新的数据到达时发射,此时执行updateServerProgress函数把接收到的数据保存到文件,并更新进度条
4.实现槽函数updateServerProgress

从上篇和此篇可看出基于客户和服务器的tcp编程:
需要在服务器端有QTcpServer监视某个端口,在客户端使用QTcpSocket发起连接,然后在服务器端获取连接QTcpSocket,然后双方就使用QTcpSocket提供的方法来互发数据其中,
发送方使用 QByteArray block; clientConnection->write(block);发送数据,使用bytesWritten信号得知数据已经发送完成
接收方使用readyRead信号得知有数据到达,使用  QDataStream in(tcpSocket); in >> message;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  qt4 tcp 应用程序