详解 QT 多线程 TCP 文件接收服务器实例(2)

“tcpserver.cpp”

 
 
  1.  #include "tcpserver.h"   
  2.  #include "tcpthread.h"   
  3.  //构造函数   
  4.  TcpServer::TcpServer(QObject *parent) :  
  5.  QTcpServer(parent)  
  6.  {  
  7. }  
  8.   //重新定义了incomingConnection这个虚函数,   
  9. //开辟一个新的tcpsocket线程,从TcpServer获得socketDescriptor,   
  10.  //并完成相应的信号连接。   
  11.  void TcpServer::incomingConnection(int socketDescriptor)  
  12. {  
  13.  TcpThread *thread = new TcpThread(socketDescriptor, this);  
  14.  //将线程结束信号与线程的deleteLater槽关联   
  15.  connect(thread, SIGNAL(finished()),  
  16.  thread, SLOT(deleteLater()));  
  17.  //关联相应的UI更新槽   
  18.  connect(thread,SIGNAL(bytesArrived(qint64,qint32,int)),  
  19. this,SIGNAL(bytesArrived(qint64,qint32,int)));   
  20. //QT的线程都是从start开始,调用run()函数   
  21.  thread->start();  
  22.  } 

极其简单的构造函数,在incomingConnection()中,定义一个线程TcpThread,并将socketDescriptor传递给其构造函数,完成线程的创建,并且调用QThread的start函数,开始执行线程的虚函数run()。

“tcpthread.h”

 
 
  1. #ifndef TCPTHREAD_H  
  2. #define TCPTHREAD_H  
  3.  #include <QThread> 
  4. #include <QTcpSocket> 
  5.  #include <QtNetwork> 
  6.  //继承QThread的TCP传输线程   
  7.  //主要是完成run()虚函数的定义   
  8.  //还有一些辅助变量的声明   
  9. class QFile;  
  10. class QTcpSocket;  
  11.  class TcpThread : public QThread  
  12.  {  
  13.  Q_OBJECT  
  14.  public:  
  15.  TcpThread(int socketDescriptor, QObject *parent);  
  16. void run();  
  17. signals:  
  18. void error(QTcpSocket::SocketError socketError);  
  19. void bytesArrived(qint64,qint32,int);  
  20.  public slots:  
  21.  void receiveFile();  
  22. private:  
  23.  int socketDescriptor;  
  24.  qint64 bytesReceived; //收到的总字节   
  25.  qint64 byteToRead; //准备读取的字节   
  26.  qint32 TotalBytes; //总共传输的字节   
  27.  QTcpSocket *tcpSocket;  
  28.  QHostAddress fileName; //文件名   
  29.  QFile *localFile;  
  30.  QByteArray inBlock; //读取缓存   
  31.  };  
  32.  #endif // TCPTHREAD_H  

继承自QThread类,在此线程中完成TCPSOCKET的建立,和文件的接收。

“tcpthread.cpp”

 
 
  1. #include "tcpthread.h"   
  2. #include <QtGui> 
  3.  #include <QtNetwork> 
  4.  //构造函数完成简单的赋值/   
  5. TcpThread::TcpThread(int socketDescriptor, QObject *parent):  
  6. QThread(parent),socketDescriptor(socketDescriptor)  
  7.  {  
  8.  bytesReceived = 0;  
  9.  }  
  10.   //因为QT的线程的执行都是从run()开始,   
  11.  //所以在此函数里完成tcpsocket的创建,相关信号的绑定   
  12.  void TcpThread::run()   
  13.  {  
  14.  tcpSocket = new QTcpSocket;  
  15. //将Server传来的socketDescriptor与刚创建的tcpSocket关联   
  16. if (!tcpSocket->setSocketDescriptor(socketDescriptor)) {  
  17. emit error(tcpSocket->error());  
  18. return;  
  19.  }  
  20. qDebug()<<socketDescriptor;   
  21. : //这是重中之重,必须加Qt::BlockingQueuedConnection!   
  22.  //这里困扰了我好几天,原因就在与开始没加,默认用的Qt::AutoConnection。   
  23.  //简单介绍一下QT信号与槽的连接方式:   
  24. //Qt::AutoConnection表示系统自动选择相应的连接方式,如果信号与槽在同一线程,就采用Qt::DirectConnection,
  25. 如果信号与槽不在同一线程,将采用Qt::QueuedConnection的连接方式。   
  26.  //Qt::DirectConnection表示一旦信号产生,立即执行槽函数。   
  27.  //Qt::QueuedConnection表示信号产生后,将发送Event给你的receiver所在的线程,postEvent(QEvent::MetaCall,...),
  28. slot函数会在receiver所在的线程的event loop中进行处理。   
  29.  //Qt::BlockingQueuedConnection表示信号产生后调用sendEvent(QEvent::MetaCall,...),
  30. 在receiver所在的线程处理完成后才会返回;只能当sender,receiver不在同一线程时才可以。   
  31.  //Qt::UniqueConnection表示只有它不是一个重复连接,连接才会成功。如果之前已经有了一个链接(相同的信号连接到同一对象的同一个槽上),那么连接将会失败并将返回false。   
  32.  //Qt::AutoCompatConnection与QT3保持兼容性   
  33.  //说明一下,对于任何的QThread来说,其线程只存在于run()函数内,其它的函数都不在线程内,所以此处要采用Qt::BlockingQueuedConnection,   
  34.  //因为当SOCKET有数据到达时就会发出readyRead()信号,但是此时可能之前的receiveFile()还未执行完毕,之前使用的Qt::AutoConnection,   
  35.  //结果传输大文件的时候就会出错,原因就在于只要有数据到达的时候,就会连接信号,但是数据接收还没处理完毕,而Qt::BlockingQueuedConnection会阻塞   
  36.  //此连接,直到receiveFile()处理完毕并返回后才发送信号。   
  37.  
  38. connect(tcpSocket, SIGNAL(readyRead()),  
  39. this, SLOT(receiveFile()),Qt::BlockingQueuedConnection);  
  40.  exec();  
  41.  
  42.  }  
  43.  void TcpThread::receiveFile()  
  44. {  
  45. //将tcpsocket封装到QDataStream里,便于使用操作符>>   
  46.  QDataStream in(tcpSocket);  
  47.  if(bytesReceived < sizeof(qint32))  
  48. {  
  49. //先接收32bit的文件大小   
  50.  if(tcpSocket->bytesAvailable() >= sizeof(qint32))  
  51. {  
  52.  63: in.setByteOrder(QDataStream::LittleEndian); //必须的,因为发送端为LINUX系统   
  53.  in>>TotalBytes;  
  54. TotalBytes += 4;  
  55. qDebug()<<TotalBytes;  
  56.    
  57.  bytesReceived += sizeof(qint32);  
  58. fileName = tcpSocket->peerAddress();  
  59.  quint16 port = tcpSocket->peerPort();  
  60.   localFile = new QFile(fileName.toString()+(tr(".%1").arg(port))); //用户端的IP地址作为保存文件名   
  61.  if (!localFile->open(QFile::WriteOnly ))  
  62.  
  63. {  
  64. }  
  65.  }   
  66.  }  
  67.  //如果读取的文件小于文件大小就继续读   
  68.  if (bytesReceived < TotalBytes){  
  69.  byteToRead = tcpSocket->bytesAvailable();  
  70.  bytesReceived += byteToRead;  
  71.  inBlock = tcpSocket->readAll();  
  72.  qDebug()<<"bytesReceived is:"<<bytesReceived;  
  73. localFile->write(inBlock);  
  74.  inBlock.resize(0);  
  75.  }   
  76. emit bytesArrived(bytesReceived,TotalBytes,socketDescriptor);  
  77.  if (bytesReceived == TotalBytes) {  
  78.  localFile->close();  
  79.  qDebug()<<bytesReceived;  
  80.  emit finished();  
  81.  QApplication::restoreOverrideCursor();  
  82.  }  
  83.  } 

代码中已经有很详细的注释,需要再说明的一点就是在多线程的编写中,信号/槽的连接方式一定要根据实际情况来进行选择!

“widget.h”

 
 
  1.  #ifndef WIDGET_H  
  2.  #define WIDGET_H  
  3.  #include <QWidget>   
  4.  #include "tcpthread.h"   
  5.  #include "tcpserver.h"    
  6. : class QDialogButtonBox;   
  7.  class QTcpSocket;  
  8.  namespace Ui {  
  9.  class Widget;  
  10.  }  
  11.    
  12. : class Widget : public QWidget  
  13. : {  
  14.  Q_OBJECT   
  15. public:  
  16.  explicit Widget(QWidget *parent = 0);  
  17.  ~Widget();  
  18.  private:  
  19.  Ui::Widget *ui;  
  20.  TcpServer tcpServer;  
  21.  private slots:  
  22.  void on_OkButton_clicked();  
  23.  void updateProgress(qint64,qint32,int);  
  24.   };   
  25.  #endif // WIDGET_H  

简单的widget类。

“widget.cpp”

 
 
  1.  #include "widget.h"   
  2. #include "ui_widget.h"   
  3.  #include <QtNetwork> 
  4.  #include <QtGui>   
  5.  Widget::Widget(QWidget *parent) :  
  6.  QWidget(parent),  
  7.  ui(new Ui::Widget)  
  8. {  
  9.  ui->setupUi(this);  
  10.  ui->progressBar->setMaximum(2);  
  11. ui->progressBar->setValue(0);  
  12.  }   
  13.  Widget::~Widget()  
  14.  {  
  15. delete ui;  
  16.  }   
  17.  void Widget::on_OkButton_clicked()  
  18.  {  
  19.  ui->OkButton->setEnabled(false);   
  20.  QApplication::setOverrideCursor(Qt::WaitCursor);  
  21.  //bytesReceived = 0;   
  22.  while (!tcpServer.isListening() && !tcpServer.listen(QHostAddress::Any,12345))  
  23.  {  
  24.  QMessageBox::StandardButton ret = QMessageBox::critical(this,  
  25. tr("回环"),  
  26.  tr("无法开始测试: %1.")  
  27.  .arg(tcpServer.errorString()),  
  28.  QMessageBox::Retry  
  29.  | QMessageBox::Cancel);  
  30.  if (ret == QMessageBox::Cancel)  
  31.  return;  
  32.  }  
  33.  ui->statuslabel->setText(tr("监听端口:%1").arg("12345"));  
  34.  connect(&tcpServer,SIGNAL(bytesArrived(qint64,qint32,int)),  
  35.  this,SLOT(updateProgress(qint64,qint32,int)));  
  36.  }  
  37.  void Widget::updateProgress(qint64 bytesReceived, qint32 TotalBytes, int socketDescriptor)  
  38.  {  
  39.  ui->progressBar->setMaximum(TotalBytes);  
  40.  ui->progressBar->setValue(bytesReceived);  
  41.  ui->statuslabel->setText(tr("已接收 %1MB")  
  42. .arg(bytesReceived / (1024 * 1024)));  
  43.  ui->textBrowser->setText(tr("现在连接的socket描述符:%1").arg(socketDescriptor));  
  44.  } 

完成服务器的监听,和进度条的更新。

点击开始后,处于监听状态。

 QT 多线程 TCP 文件接收服务器实例

传输文件时:

 QT 多线程 TCP 文件接收服务器实例

小结:关于详解 QT 多线程 TCP 文件接收服务器实例的内容介绍完了,关于QT 网络的内容,如果你有兴趣的话,请参考编辑推荐。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值