Qt实现客户端与服务器消息发送与文件传输(二)

2022-07-24 15:35:24 浏览数 (1)

引言

客户端与服务器之间的数据传送在很多案例场景里都会有应用。这里Jungle用Qt来简单设计实现一个场景,即:

①两端:服务器QtServer和客户端QtClient

②功能:服务端连接客户端,两者能够互相发送消息,传送文件,并且显示文件传送进度。

环境:VS2008 Qt4.8.6 Qt设计

本文紧接着上一篇文章

04

客户端实现

代码语言:javascript复制
void QtClient::sendFile()
{
  this->localFile = new QFile(filename);
  if(!localFile->open(QFile::ReadOnly))
  {
    ui.textEdit->append(tr("Client:open file error!"));
    return;
  }
  ///获取文件大小
  this->totalBytes = localFile->size();
  QDataStream sendout(&outBlock,QIODevice::WriteOnly);
  sendout.setVersion(QDataStream::Qt_4_8);
  QString currentFileName = filename.right(filename.size()-filename.lastIndexOf('/')-1);
  
  qDebug()<<sizeof(currentFileName);
  ////保留总代大小信息空间、文件名大小信息空间、文件名
  sendout<<qint64(0)<<qint64(0)<<currentFileName;
  totalBytes  = outBlock.size();
  sendout.device()->seek(0);
  sendout<<totalBytes<<qint64((outBlock.size()-sizeof(qint64)*2));

  bytestoWrite = totalBytes-fileSocket->write(outBlock);
  outBlock.resize(0);
}

这里同样说明两点:

①setVision():设定数据序列的版本,官方文档里说明这个不是必须的,但是推荐我们要去进行这一步的工作。我这里是Qt4.8.6,所以设定为Qt4.8.见下图(截自Qt官方文档)

②qint64:这个类型在Jungle之前的博客里也提到过,是指qt的无符号的整型,64位

代码语言:javascript复制
void QtClient::updateFileProgress(qint64 numBytes)
{
  ////已经发送的数据大小
  bytesWritten  = (int)numBytes;

  ////如果已经发送了数据
  if(bytestoWrite > 0)
  {
    outBlock = localFile->read(qMin(bytestoWrite,perDataSize));
    ///发送完一次数据后还剩余数据的大小
    bytestoWrite -= ((int)fileSocket->write(outBlock));
    ///清空发送缓冲区
    outBlock.resize(0);
  }
  else
    localFile->close();

  ////更新进度条
  this->ui.progressBar->setMaximum(totalBytes);
  this->ui.progressBar->setValue(bytesWritten);

  ////如果发送完毕
  if(bytesWritten == totalBytes)
  {
    localFile->close();
    //fileSocket->close();
  }
}

void QtClient::updateFileProgress()
{
  QDataStream inFile(this->fileSocket);
  inFile.setVersion(QDataStream::Qt_4_8);

  ///如果接收到的数据小于16个字节,保存到来的文件头结构
  if(bytesReceived <= sizeof(qint64)*2)
  {
    if((fileSocket->bytesAvailable()>=(sizeof(qint64))*2) && (filenameSize==0))
    {
      inFile>>totalBytes>>filenameSize;
      bytesReceived  = sizeof(qint64)*2;
    }
    if((fileSocket->bytesAvailable()>=filenameSize) && (filenameSize != 0))
    {
      inFile>>filename;
      bytesReceived  = filenameSize;
      filename = "ServerData/" filename;
      localFile = new QFile(filename);
      if(!localFile->open(QFile::WriteOnly))
      {
        qDebug()<<"Server::open file error!";
        return;
      }
    }
    else
      return;
  }
  /////如果接收的数据小于总数据,则写入文件
  if(bytesReceived < totalBytes)
  {
    bytesReceived  = fileSocket->bytesAvailable();
    inBlock = fileSocket->readAll();
    localFile->write(inBlock);
    inBlock.resize(0);
  }

  ////数据接收完成时
  if(bytesReceived == totalBytes)
  {
    this->ui.textEdit->append("Receive file successfully!");
    bytesReceived = 0;
    totalBytes = 0;
    filenameSize = 0;
    localFile->close();
    //fileSocket->close();
  }
}

05

服务端实现

类的设计:

代码语言:javascript复制
class QtServer : public QWidget
{
  Q_OBJECT

public:
  QtServer(QWidget *parent = 0, Qt::WFlags flags = 0);
  ~QtServer();

  QTcpServer *server;
  QTcpSocket *socket;
  QTcpServer *fileserver;
  QTcpSocket *filesocket;

private slots:  
  void sendMessage(); 
  void acceptConnection();
  ////接收客户端发送的数据
  void receiveData();

  void acceptFileConnection();
  void updateFileProgress();
  void displayError(QAbstractSocket::SocketError socketError);

  ///选择发送的文件
  void selectFile();
  void sendFile();
  ////更新文件传送进度
  void updateFileProgress(qint64);

private:
  Ui::QtServerClass ui;

  ////传送文件相关变量
  qint64 totalBytes;
  qint64 bytesReceived;
  qint64 bytestoWrite;
  qint64 bytesWritten;
  qint64 filenameSize;
  QString filename;
  ///每次发送数据大小
  qint64 perDataSize;
  QFile *localFile;
  ////本地缓冲区
  QByteArray inBlock;
  QByteArray outBlock;

  ////系统时间
  QDateTime current_date_time;
  QString str_date_time;
};

实现:

代码语言:javascript复制
#include "qtserver.h"
#include <QDataStream>
#include <QMessageBox>
#include <QString>
#include <QByteArray>

QtServer::QtServer(QWidget *parent, Qt::WFlags flags)
  : QWidget(parent, flags)
{
  ui.setupUi(this);

  this->socket = new QTcpSocket(this);
  this->server = new QTcpServer(this);
  ///开启监听
  this->server->listen(QHostAddress::Any,6666);
  connect(this->server,SIGNAL(newConnection()),this,SLOT(acceptConnection()));
  connect(ui.pushButton_send,SIGNAL(clicked()),this,SLOT(sendMessage()));

  ///文件传送套接字
  this->filesocket = new QTcpSocket(this);
  this->fileserver = new QTcpServer(this);
  this->fileserver->listen(QHostAddress::Any,8888);
  connect(this->fileserver,SIGNAL(newConnection()),this,SLOT(acceptFileConnection()));

  //// 文件传送相关变量初始化
  bytesReceived = 0;
  totalBytes = 0;
  filenameSize = 0;
  connect(ui.pushButton_selectFile,SIGNAL(clicked()),this,SLOT(selectFile()));
  connect(ui.pushButton_sendFile,SIGNAL(clicked()),this,SLOT(sendFile()));
}

QtServer::~QtServer()
{

}

void QtServer::acceptConnection()
{
  ////返回一个socket连接
  this->socket = this->server->nextPendingConnection();
  connect(socket,SIGNAL(readyRead()),this,SLOT(receiveData()));
}

void QtServer::acceptFileConnection()
{
  bytesWritten = 0;
  ///每次发送数据大小为64kb
  perDataSize = 64*1024;
  this->filesocket = this->fileserver->nextPendingConnection();
  ///接受文件
  connect(filesocket,SIGNAL(readyRead()),this,SLOT(updateFileProgress()));  
  connect(filesocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(updateFileProgress(qint64)));
  connect(filesocket,SIGNAL(bytesWritten(qint64)),this,SLOT(displayError(QAbstractSocket::SocketError socketError)));
}

void QtServer::sendMessage()
{
  this->socket->write(ui.lineEdit->text().toLatin1());
  ////显示
  current_date_time = QDateTime::currentDateTime();
  str_date_time = current_date_time.toString("yyyy-MM-dd hh:mm:ss");
  QString str = "You " str_date_time "n" ui.lineEdit->text();
  ui.browser->append(str);
}

void QtServer::receiveData()
{
  /////获取当前时间
  current_date_time = QDateTime::currentDateTime();
  str_date_time = current_date_time.toString("yyyy-MM-dd hh:mm:ss") "n";

  ////接收数据
  QString str = this->socket->readAll();

  ////显示
  str = "Client " str_date_time str;
  this->ui.browser->append(str);
}

void QtServer::updateFileProgress()
{
  QDataStream inFile(this->filesocket);
  inFile.setVersion(QDataStream::Qt_4_8);
  
  ///如果接收到的数据小于16个字节,保存到来的文件头结构
  if(bytesReceived <= sizeof(qint64)*2)
  {
    if((filesocket->bytesAvailable()>=(sizeof(qint64))*2) && (filenameSize==0))
    {
      inFile>>totalBytes>>filenameSize;
      bytesReceived  = sizeof(qint64)*2;
    }
    if((filesocket->bytesAvailable()>=filenameSize) && (filenameSize != 0))
    {
      inFile>>filename;
      bytesReceived  = filenameSize;
      ////接收的文件放在指定目录下
      filename = "ClientData/" filename;
      localFile = new QFile(filename);
      if(!localFile->open(QFile::WriteOnly))
      {
        qDebug()<<"Server::open file error!";
        return;
      }
    }
    else
      return;
  }
  /////如果接收的数据小于总数据,则写入文件
  if(bytesReceived < totalBytes)
  {
    bytesReceived  = filesocket->bytesAvailable();
    inBlock = filesocket->readAll();
    localFile->write(inBlock);
    inBlock.resize(0);
  }
  ////更新进度条显示
  this->ui.progressBar_fileProgress->setMaximum(totalBytes);
  this->ui.progressBar_fileProgress->setValue(bytesReceived);
  ////数据接收完成时
  if(bytesReceived == totalBytes)
  {
    this->ui.browser->append("Receive file successfully!");
    bytesReceived = 0;
    totalBytes = 0;
    filenameSize = 0;
    localFile->close();
    //filesocket->close();
  }
}

void QtServer::displayError(QAbstractSocket::SocketError socketError)
{
  qDebug()<<socket->errorString();
  socket->close();
}

void QtServer::selectFile()
{
  filesocket->open(QIODevice::WriteOnly);
  ////文件传送进度更新
  connect(filesocket,SIGNAL(bytesWritten(qint64)),this,SLOT(updateFileProgress(qint64)));

  this->filename = QFileDialog::getOpenFileName(this,"Open a file","/","files (*)");
  ui.lineEdit_fileName->setText(filename);
}

void QtServer::sendFile()
{
  this->localFile = new QFile(filename);
  if(!localFile->open(QFile::ReadOnly))
  {
    return;
  }
  ///获取文件大小
  this->totalBytes = localFile->size();
  QDataStream sendout(&outBlock,QIODevice::WriteOnly);
  sendout.setVersion(QDataStream::Qt_4_8);
  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));

  bytestoWrite = totalBytes-filesocket->write(outBlock);
  outBlock.resize(0);
}

void QtServer::updateFileProgress(qint64 numBytes)
{
  ////已经发送的数据大小
  bytesWritten  = (int)numBytes;

  ////如果已经发送了数据
  if(bytestoWrite > 0)
  {
    outBlock = localFile->read(qMin(bytestoWrite,perDataSize));
    ///发送完一次数据后还剩余数据的大小
    bytestoWrite -= ((int)filesocket->write(outBlock));
    ///清空发送缓冲区
    outBlock.resize(0);
  }
  else
    localFile->close();

  ////如果发送完毕
  if(bytesWritten == totalBytes)
  {
    localFile->close();
    //filesocket->close();
  }
}

06

测试

http://mpvideo.qpic.cn/0af2c3lezq3veayob4aqobqbayff5wfdoctfyeyrbaga4danbada.f10002.mp4?

0 人点赞