本系列所有文章可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873
Qt官方Demo中,Fortune(财富) Example很多,包括系列1中的Fortune Server Example, Fortune Client Example, 系列4中的Blocking Fortune Client Example, 系列5中的Threaded Fortune Server Example。有时候归类学习是一个挺不错的方法,它能迅速帮你理清思路。
这里画了几个图来描绘它们各自的实现过程,为了简明,只描述了主要的部分。
在Fortune Server/Client Example中,tcpSocket做为类成员变量写在GUI线程中,Server服务器通过调用tcpsocket的Listen()监听是否有来自客户端的连接,
当Client客户端通过connectToHost()连接到Server后,Server端由QTcpSocket *clientConnection = tcpServer->nextPendingConnection()创建了基于
该连接对象的套接字,通过write(block)向客户端发送数据。
这种方式好不好呢,好,简单明了,代码也少。但是如果发送的数据太频繁,太大,对于主线程的压力是可想而知的。在实际工程中,数据通信也多是放在单独的线程使用。
那么多线程的客户端怎么实现呢?
Blocking Fortune Client有两个主类,一个是BlockingClient,继承自QDialog,负责窗体的搭建,响应用户操作等,说白了就是GUI主线程。还有一个FortuneThread类继承自QThread,QTcpSocket对象创建在FortuneThread的run()函数中。用户需要接受数据的时候,通过BlockingClient向FortuneThread
发送一个请求,通过线程连接到主机,FortuneThread接收到来自主机的数据再通过信号槽机制返回给主线程。像这种客户端使用阻塞模式是很常见的,因为它只有一个Socket,而且使用阻塞网络的API逻辑上更为简单。但是能在GUI主线程上使用阻塞吗?当然不行,界面一卡死用户骂不死你。。。还有一点可以看出,由于部分工作由次线程接管,主界面的代码量明显减少,这是模块化编程所提倡的。
回到线程上面来,BlockClient并没有在每次RequestNewFortune()时创建一个新的FortuneThread,而是调用一个全局的thread来进行操作,这样必然带来竞争,解决的办法是使用互斥锁(QMutex)。在这种机制下,FortuneThread一次只能处理一个请求,不过对于客户机而言,一般也够了。
Thread Fortune Server Example里面的类就更多啦,除了界面类和线程类,还有一个FortuneServer类继承自QTcpServer。为什么不像上面那个例子一样直接在线程创建一个QTcpServer对象呢?因为服务器与客户机不同,它面对往往都是多个客户机并发的请求。如果像上一个例子那样先锁住资源响应第一个,再解锁,再锁住响应第二个,再响应第三个。。。。好吧这个服务器这是太棒了。。。那么此时我们需要对每一个客户机的请求创建一个单独的线程来进行响应。此时我们不再需要互斥锁来保护资源。
Dialog类负责界面绘制,并通过server.listen()开启FortuneServer的监听事件,一旦有客户机连接上来,server通过IncomingConnection()开启一个新的线程FortuneThread,该线程则负责将财富(Fortune)发送给客户端,之后将自身销毁。
好了,就先到这里吧~