Qt封装QTcpServer参考资料--QT4中构建多线程的服务器

首先说一下对 多线程这个名词的理解过程。之前据说过不少次多线程这个词,并且每每与 服务器联系起来,所以一直把多线程误解为服务器特有的功能;直到此次 课程设计,仔细 学习了一下多线程的机制,才知道真正的意思。简单的来讲,就是同时有多个线程一块儿运行,而不一样的线程能够执行不一样的操做。举个例子,一个图像处理工具,能够用鼠标一边移动图像,一边用快捷键缩放图像,此时,移动图像、缩放图像就是不一样的线程来处理的,若是不支持多线程而是单线程的,那么只能挨个操做了。

 

而对于服务器来讲,多线程的这个特性太有用了,由于多线程使得服务器可能同时响应多个客户端的请求,因此如今服务器大多采用多线程,因此才会形成我开始的误解。html

不论是多线程,仍是服务器,QT中已经封装好了特定的类,因此使用起来也很方便。服务器

下面创建一个支持多线程、TCP的服务器。数据结构

首先创建一个服务器。新建一个类(Server)继承QT中的QTcpServer类便可。服务器的职责是监听端口。当监听到有客户端试图与服务器创建链接的时候,分配socket与客户端链接,再进行数据通讯。QTcpServer的listen()方法执行监听过程,能够指定监听的地址和端口。若给定了QHostAddress类型的监听地址,则监听该地址,不然,监听全部地址;若给定了quint16类型的监听端口,则监听该端口,不然,随机选定一个监听端口。多线程

  1. Server * server = new Server;   
  2. if(!server->listen(host,port)){   
  3. ...//error   
  4. }  

QTcpServer有一个虚函数incomingConnection(int socketDescriptor),服务器每当监听到一个客户端试图创建链接的时候,会自动调用这个函数,所以,处理这个请求的过程就能够在这个函数中定义,即在子类Server的定义阶段,从新定义incomingConnection()这个函数。对于一个多线程的服务器,每当客户端试图链接的时候,服务器应该启动一个线程,负责对这个客户端进行服务,因此,incomingConnection()这个函数所要作的就是创建一个线程,而所创建的线程的做用就是对客户端进行服务,而这其中创建socket链接是基础。服务器在监听到客户端试图创建socket链接时,会为此socket分配一个惟一的标识socketDescriptor,这个标识将在服务器端创建socket链接时使用,因此应提供给每个线程。socket

在QT中使用多线程,创建一个类(Thread)继承QThread类便可。QThread类也有一个虚函数,这个函数是run(),线程创建并启动(QThread::start())后,就会执行这里面的代码,所以,线程的逻辑过程就应该在run()里面定义。服务器的线程要根据socketDescriptor标识的socket创建链接,而后进行数据通讯,因此要将socketDescriptor传入到Thread中,前面说过,线程是在incomingConnection()里面创建,用构造函数将socketDescriptor传入Thread类,再用socketDescriptor创建socket链接。tcp

定义incomingConnection()函数

  1. void incomingConnection(int socketDescriptor){   
  2.      Thread * thread = new Thread(socketDescriptor);   
  3.     thread->start();   
  4. }  

定义run()工具

  1. void run(){   
  2.      QTcpSocket * socket = new QTcpSocket(socketDescriptor);   
  3.      ...//数据通讯   
  4. }  

自此,一个简单的多线程服务器创建完毕。学习

写的很差,望请指教。ui

============================
QT4中socket通讯

近的软件工程课程设计让我从新开始使用QT,上次数据结构的课程设计也是用QT,虽然是作出来了,可是如今想一想,那个时候对QT的理解,或者说得更广一点,对OO的理解,简直太差劲了,固然,人的知识是进步的,因此如今有这样的感觉是很正常的。虽然总体的开发工做尚未彻底结束,可是已经有了不少心得体会,因此特来记录分享一下。

 

咱们的系统采用的是C/S结构,因此客户端与服务器通讯是最关键,不幸的是,虽然咱们没有用过QT的socket类,咱们也没有估计好通讯的难度,等到意识到第一次使用的困难时,已是第5天了,始终没有进展,我临危受命。如今是第6天,刚刚把通讯模块封装好,算是对这两天的突击的一个回报。

咱们遇到的问题socket已经创建,而且发送端已经将消息发送,可是接收端始终收不到消息。(我用的socket类型是TCP,也就是QTcpSocket类)

发送端(发送端一直不存在问题)代码以下:

  1. ... // 创建链接,客户端和服务器端有区别,在此省略<BR>   
  2. QByteArray block;   
  3. QDataStream out(&block,QIODevice::WriteOnly);  // 写信息至block中,用到QDataStream类<BR>   
  4. socket.write(block);// 信息写完毕,写入socket,由socket发送<BR>   
  5. socket.disconnectFormHost();   
  6. socket.waitForDisconnected();  

有问题的接收端代码以下:

  1. ... // 创建链接<BR>   
  2. QDataStream in(&socket);// 接收socket中的数据流<BR>   
  3. ... // 从数据流 in 中读数据  

以上是最原始的接收和发送端工做过程,调试过程当中,分别讲两端的socket的状态打印出来,结果是发送端为A connection is established. 而接收端为The socket has started establishing a connection. 也就是说发送端正确的创建了链接,并将数据写入,而接收端只是正在创建链接,而并无创建好,因此是根本不会受到数据的。因此先要确保接收端的链接创建好。waitForConnected()方法就能够解决这个问题,它将一直等待直到链接已经创建。

改进后的接收端代码:

  1. ... // 创建链接<BR>   
  2. socket.waitForConnected(5000) // 5000表示等待的时间,默认参数为3000,单位是百万分之一秒   
  3. QDataStream in(&socket);// 接收socket中的数据流<BR>   
  4. ... // 从数据流 in 中读数据  

此时,接收端输出的socket状态为A connection is established,链接成功创建。
可是仍是收不到信息,参考了一下别人的程序,再比对一下参考手册,原来QTcpSocket的爷爷类(实际上是父类QAbstractSocket的父类)QIODevice有一个readyRead的信号(signal),当信息准备好并能够读的时候,这个信号就将发出,也就是说,只有当这个信号发出的时候,才能够读消息。因此要把读消息的动做read做为一个槽(slot),并将其与readyRead信号链接。

  1. connect(&socket,SIGNAL(readyRead()),this,SLOT(read()));  

可是直接触发socket信号,而不用图形界面的动做来触发一个动做并由这个动做来触发socket信号一直也触发不了read这个动做。可是我要封装成一个接口类提供给上层使用,用图形界面天然是不现实的,因而翻阅了手册,发现了一个QAbstractSocket类的一个方法——waitForReadyRead(),这个方法将一直等待到数据能够读时结束,此时就能够读数据了。方法也很简单:

  1. ... // 创建链接<BR>   
  2. socket.waitForConnected(5000) // 5000表示等待的时间,默认参数为3000,单位是百万分之一秒<BR>   
  3. if(!socket.waitForReadyRead(3000)){//3000为等待时间,没有默认的等待时间,单位是百万分之一秒   
  4. return ;   
  5. }<BR>   
  6. QDataStream in(&socket);// 接收socket中的数据流<BR>   
  7. ... // 从数据流 in 中读数据  

这样,数据成功读取出来,实现数据的通讯。

单向的数据传输问题解决了,而后再利用单向的数据通讯组装成双向的数据通讯,这过程当中也会遇到很多问题,将在另外一篇日志介绍。

转自:https://blog.csdn.net/dongfangyu/article/details/5919789

jerry在我本身编写qt4.8 多线程tcp服务端的时候未参考,后来看到的,还不错,供你们参考,重要的我标红了,其实原理都是相同的,就是多线程我习惯用movetothread。

相关文章
相关标签/搜索