前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Qt实现客户端与服务器消息发送与文件传输(二)

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

作者头像
用户6557940
发布2022-07-24 15:35:24
2.2K0
发布2022-07-24 15:35:24
举报
文章被收录于专栏:Jungle笔记

引言

客户端与服务器之间的数据传送在很多案例场景里都会有应用。这里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?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Jungle笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档