QT tcp服务器及客户端 传输图片及显示

上一节里我们使用TCP服务器和客户端相互发送字符串信息,本节间学习图片的传输及显示。

一、服务器端

程序开始进行监听,一旦发现有连接请求就发出newConnection()信号,然后我们便接受连接,可以接收数据。

步骤1.新建QtGui应用

名称为tcpServer,基类选择QWidget,类名为Widget,完成后打开tcpReceiver.pro添加一行代码:QT += network

步骤2.我们更改widget.ui文件,设计界面如下。

其中“服务器端”LabelobjectNamestatuslab;进度条ProgressBarobjectNameprogressBar,设置其value属性为0;发送按钮的objectNamesendButton;图片显示textlabel的objectName为recLab。

效果如下。

步骤3.更改widget.h文件的内容。

  1. 添加头文件包含:#include <QtNetwork>

2)添加私有变量:

   QTcpServer *tcpServer;

    QTcpSocket *currentClient;

    qint64 totalBytes//存放总大小信息

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

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

    QString fileName;   //存放文件名

    QFile *localFile;   //本地文件

    QByteArray inBlock;   //接收数据缓冲区

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

    qint64 bytesToWrite;   //剩余数据大小

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

    QByteArray outBlock;  //数据缓冲区

3)添加私有槽函数:

private slots:

   void NewConnection();

    void recMessage();

    void sendMessage();

    void disconnect();

    void continueSend(qint64);

    void on_sendButton_clicked();

步骤4.更改widget.cpp文件。

1)在构造函数中添加代码:

totalBytes = 0;

    bytesReceived = 0;

fileNameSize = 0;

totalBytes = 0;

    bytesReceived = 0;

    fileNameSize = 0;

    tcpServer = new QTcpServer(this);

    if(!tcpServer->listen(QHostAddress::Any,6666))

    {  //**本地主机的6666端口,如果出错就输出错误信息,并关闭

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

        close();

    }

    //连接信号和相应槽函数,有新的连接进入是需处理

    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(NewConnection()));

    ui->sendButton->setEnabled(false);

(2)实现接受连接函数。

void Widget::NewConnection()

{

    //初始化为0;

    //blockSize=0;

   // inBlock.resize(0);

    //新连接进入的显示处理

    currentClient = tcpServer->nextPendingConnection();

    ui->statuslab->setText(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1])\

                                              .arg(currentClient->peerPort()));

    connect(currentClient, SIGNAL(readyRead()), this, SLOT(recMessage()));

    connect(currentClient, SIGNAL(disconnected()), this, SLOT(disconnect()));

    //当有数据发送成功时,继续发送

    connect(currentClient,SIGNAL(bytesWritten(qint64)),this, SLOT(continueSend(qint64)));

    ui->sendButton->setEnabled(true);

}

我们要先发送文件的总大小,然后文件名长度,然后是文件名,这三部分合称为文件头结构,最后再发送文件数据。所以在发送函数里就要进行相应的处理,当然,在服务器的接收函数里也要进行相应的处理。对于文件大小,这次使用了qint64,它是64位的,可以表示一个很大的文件。

(3)开始发送数据(文件名,文件大小等信息)

void Widget::sendMessage()

{

    bytesWritten = 0;

    fileName = QFileDialog::getOpenFileName(this);

    if(!fileName.isEmpty())

      {

        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_5_8);

            QString currentFileName = fileName.right(fileName.size()

        - fileName.lastIndexOf('/')-1);

            //依次写入总大小信息空间,文件名大小信息空间,文件名

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

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

            totalBytes += outBlock.size();

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

            //返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间

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

            //发送完头数据后剩余数据的大小

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

            ui->statuslab->setText(tr("开始发送"));

            outBlock.resize(0);

      }

 }

(4)分块发送,更新进度条

void Widget::continueSend(qint64 numBytes)

 {

     //已经发送数据的大小

         bytesWritten += (int)numBytes;

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

         {

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

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

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

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

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

            //清空发送缓冲区

            outBlock.resize(0);

         } else {

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

         }

         //更新进度条

         ui->progressBar->setMaximum(totalBytes);

         ui->progressBar->setValue(bytesWritten);

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

         {

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

            localFile->close();

         }

}

(5)发送按钮

void Widget::on_sendButton_clicked()

{

    //发送数据

    sendMessage();

}

(6)接收及显示

void Widget::recMessage()

{

    QDataStream in(currentClient);

    in.setVersion(QDataStream::Qt_5_8);

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

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

         if((currentClient->bytesAvailable() >= sizeof(qint64)*2)

               && (fileNameSize == 0))

           { //接收数据总大小信息和文件名大小信息

               in >> totalBytes >> fileNameSize;

               bytesReceived += sizeof(qint64) * 2;

           }

           if((currentClient->bytesAvailable() >= fileNameSize)

               && (fileNameSize != 0))

           {  //接收文件名,并建立文件

               in >> fileName;

               ui->statuslab->setText(tr("接收文件 %1 ...").arg(fileName));

               bytesReceived += fileNameSize;

               ui->statuslab->setText(fileName);

               localFile= new QFile(fileName);

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

               {

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

                    return;

               }

           }

           else return;

       }

       if(bytesReceived < totalBytes)

       {  //如果接收的数据小于总数据,那么写入文件

          bytesReceived += currentClient->bytesAvailable();

          inBlock+= currentClient->readAll();

       }

    //更新进度条

       ui->progressBar->setMaximum(totalBytes);

       ui->progressBar->setValue(bytesReceived);

       if(bytesReceived == totalBytes)

       { //接收数据完成时

           //接收显示

           QBuffer buffer(&inBlock);

           buffer.open(QIODevice::ReadOnly);

           QImageReader reader(&buffer,"jpg");

           QImage image = reader.read();

           if(!image.isNull())

           {

               image=image.scaled(ui->recLab->size());

               ui->recLab->setPixmap(QPixmap::fromImage(image));

           }

        localFile->write(inBlock);

        localFile->close();

        inBlock.resize(0);

        //重新置0 准备下次接收

        totalBytes = 0;

        bytesReceived = 0;

        fileNameSize = 0;

         ui->statuslab->setText(tr("接收文件 %1 成功!").arg(fileName));

       }

}

二、客户端

客户端里需要与服务器进行连接,一旦连接成功,才可以进行文件传输。

步骤1.新建QtGui项目

名称为tcpSender,基类选择QWidget,类名为Widget,完成后打开tcpSender.pro添加一行代码:QT += network

步骤2.我们在widget.ui文件中将界面设计如下。

这里“主机”后的Line EditobjectNameipEdit;“端口”后的Line EditobjectNameportEdit;下面的Progress BarobjectNameprogressBar,其value属性设为24;“状态”LabelobjetNamestatusLab;“打开”按钮的objectNameconBun;“发送”按钮的objectNamesendBun
 

步骤3.widget.h文件中进行更改。

1)添加头文件包含#include <QtNetwork>

2)添加private变量:

QTcpSocket *tcpSocket;

    QFile *localFile//要发送的文件

    qint64 totalBytes//数据总大小

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

    qint64 bytesToWrite;   //剩余数据大小

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

    QString fileName//保存文件路径

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

    QByteArray inBlock;   //数据缓冲区,接收

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

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

3)添加私有槽函数:

private slots: 
void newConnect(); //连接服务器

    void readData();  //接收数据

    void sendData();//发送数据

    void continueSend(qint64 numBytes); //继续并更新发送进度条

    void on_conBtn_clicked();

    void on_sendbtn_clicked();

步骤4.widget.cpp文件中进行更改

添加头文件:#include <QFileDialog>

1)在构造函数中添加代码:

tcpSocket = new QTcpSocket(this);

    loadSize = 4*1024;

    totalBytes = 0;

    bytesWritten = 0;

    bytesToWrite = 0;

    //接收

    bytesReceived = 0;

    fileNameSize = 0;

    //当有数据发送成功时,继续发送

    connect(tcpSocket,SIGNAL(bytesWritten(qint64)),this,

           SLOT(continueSend(qint64)));

    //接收数据

    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readData()));

    ui->sendbtn->setEnabled(false);

该函数将在下面的“打开”按钮单击事件槽函数中调用。

(2)实现连接主机函数。

void Widget::newConnect()

{

  //  blockSize = 0; //初始化其为0

    tcpSocket->abort(); //取消已有的连接

    //连接到主机,这里从界面获取主机地址和端口号

    tcpSocket->connectToHost(ui->hostEdit->text(), ui->portEdit->text().toInt());

    ui->sendbtn->setEnabled(true);

}该函数将在“发送”按钮的单击事件槽函数中调用。

void Widget::on_conBtn_clicked()

{

    newConnect();

}

(4)实现文件头结构的发送。

void Widget::sendData()

{

   bytesWritten = 0;

   fileName = QFileDialog::getOpenFileName(this);

   if(!fileName.isEmpty())

     {

       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_5_8);

           QString currentFileName = fileName.right(fileName.size()

       - fileName.lastIndexOf('/')-1);

           //依次写入总大小信息空间,文件名大小信息空间,文件名

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

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

           totalBytes += outBlock.size();

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

           //返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间

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

           //发送完头数据后剩余数据的大小

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

           ui->statuslab->setText(tr("开始发送"));

           outBlock.resize(0);

     }

}

5)发送文件数据。

void Widget::continueSend(qint64 numBytes)

{

    //已经发送数据的大小

        bytesWritten += (int)numBytes;

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

        {

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

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

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

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

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

           //清空发送缓冲区

           outBlock.resize(0);

        } else {

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

        }

        //更新进度条

        ui->progressBar->setMaximum(totalBytes);

        ui->progressBar->setValue(bytesWritten);

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

        {

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

           localFile->close();

        }

}

(6)接收数据并显示。

void Widget::readData()

{

    QDataStream in(tcpSocket);

    in.setVersion(QDataStream::Qt_5_8);

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

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

         if((tcpSocket->bytesAvailable() >= sizeof(qint64)*2)

               && (fileNameSize == 0))

           { //接收数据总大小信息和文件名大小信息

               in >> totalBytes >> fileNameSize;

               bytesReceived += sizeof(qint64) * 2;

           }

           if((tcpSocket->bytesAvailable() >= fileNameSize)

               && (fileNameSize != 0))

           {  //接收文件名,并建立文件

               in >> fileName;

              ui->statuslab->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 += tcpSocket->bytesAvailable();

          inBlock+= tcpSocket->readAll();

       }

    //更新进度条

       ui->progressBar->setMaximum(totalBytes);

       ui->progressBar->setValue(bytesReceived);

       if(bytesReceived == totalBytes)

       { //接收数据完成时

           //接收显示

           QBuffer buffer(&inBlock);

           buffer.open(QIODevice::ReadOnly);

           QImageReader reader(&buffer,"jpg");

           QImage image = reader.read();

           if(!image.isNull())

           {

               image=image.scaled(ui->recLab->size());

               ui->recLab->setPixmap(QPixmap::fromImage(image));

           }

        localFile->write(inBlock);

        localFile->close();

        inBlock.resize(0);

        //重新置0 准备下次接收

        totalBytes = 0;

        bytesReceived = 0;

        fileNameSize = 0;

    ui->statuslab->setText(tr("接收文件 %1 成功!").arg(fileName));

       }

}

源码下载:

https://download.csdn.net/download/delphi863/20478903

  • 8
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值