目的:每个客户端连接的tcpSocket分别分配一个专门的线程来处理。
实现时分别继承QTcpServer和QTcpScoket实现出自己需要的类。
继承QTcpServer为每个客户端连接时分配线程,并接受处理tcpScoket的信号和槽、、还有发送信息,储存连接信息等。
继承QTcpScoket为处理通信数据和增加信号的参数,以便和tcpServer更好的配合。
首先是继承并重写QTcpServer的incomingConnection函数去自己实现tcpsocket连接的建立和分配。
其文档的默认描述为:
This virtual function is called by QTcpServer when a new connection is available. The socketDescriptor argument is the native socket descriptor for the accepted connection.
The base implementation creates a QTcpSocket, sets the socket descriptor and then stores the QTcpSocket in an internal list of pending connections. Finally newConnection() is emitted.
Reimplement this function to alter the server's behavior when a connection is available.
If this server is using QNetworkProxy then the socketDescriptor may not be usable with native socket functions, and should only be used with QTcpSocket::setSocketDescriptor().
Note: If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the socketDescriptor to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method.
译文(谷歌翻译和自己简单的更正):
当QTcpServer有一个新的连接时这个虚函数被调用。该socketDescriptor参数是用于接受连接的本地套接字描述符。
该函数会创建一个QTcpSocket,并设置套接字描述符为socketDescriptor,然后存储QTcpSocket在挂起连接的内部清单。最后newConnection()被发射。
重新实现这个函数来改变服务器的行为,当一个连接可用。
如果该服务器使用QNetworkProxy那么socketDescriptor可能无法与原生socket函数使用,并且只能用QTcpSocket:: setSocketDescriptor()中使用。
注意:如果你想处理在另一个线程一个新的QTcpSocket对象传入连接,您必须将socketDescriptor传递给其他线程,并创建了QTcpSocket对象存在并使用其setSocketDescriptor()方法。
所以我们必须先重写这个函数:
下面先附上继承QTcpServer的自己的类声明,代码注释个人以为挺详细了:
01 |
//继承QTCPSERVER以实现多线程TCPscoket的服务器。 |
02 |
class MyTcpServer : public QTcpServer |
06 |
explicit MyTcpServer(QObject *parent = 0); |
09 |
void connectClient(const int , const QString & ,constquint16 );//发送新用户连接信息 |
10 |
void readData(const int,const QString &, quint16, const QByteArray &);//发送获得用户发过来的数据 |
11 |
void sockDisConnect(const int ,const QString &,const quint16 );//断开连接的用户信息 |
12 |
void sentData(const QByteArray &,const int);//向scoket发送消息 |
14 |
void setData(const QByteArray & data, const int handle);//想用户发送消息 |
15 |
void readDataSlot(const int, const QString &, const quint16,const QByteArray &);//发送获得用户发过来的数据 |
16 |
void sockDisConnectSlot(int handle, QString ip, quint16 prot);//断开连接的用户信息 |
19 |
void incomingConnection(qintptr socketDescriptor);//覆盖已获取多线程 |
21 |
QMap<int,myTcpSocket *> * tcpClient; |
接着是我们重写void QTcpServer::incomingConnection(qintptr socketDescriptor)的实现:
01 |
void MyTcpServer::incomingConnection(qintptr socketDescriptor) |
03 |
myTcpSocket * tcpTemp = new myTcpSocket(socketDescriptor); |
04 |
QThread * thread = new QThread(tcpTemp);//把线程的父类设为连接的,防止内存泄漏 |
05 |
//可以信号连接信号的,我要捕捉线程ID就独立出来函数了,使用中还是直接连接信号效率应该有优势 |
06 |
connect(tcpTemp,&myTcpSocket::readData,this,&MyTcpServer::readDataSlot);//接受到数据 |
07 |
connect(tcpTemp,&myTcpSocket::sockDisConnect,this,&MyTcpServer::sockDisConnectSlot);//断开连接的处理,从列表移除,并释放断开的Tcpsocket |
08 |
connect(this,&MyTcpServer::sentData,tcpTemp,&myTcpSocket::sentData);//发送数据 |
09 |
connect(tcpTemp,&myTcpSocket::disconnected,thread,&QThread::quit);//断开连接时线程退出 |
10 |
tcpTemp->moveToThread(thread);//把tcp类移动到新的线程 |
11 |
thread->start();//线程开始运行 |
13 |
tcpClient->insert(socketDescriptor,tcpTemp);//插入到连接信息中 |
14 |
qDebug() <<"incomingConnection THREAD IS:" <<QThread::currentThreadId(); |
16 |
emit connectClient(tcpTemp->socketDescriptor(),tcpTemp->peerAddress().toString(),tcpTemp->peerPort()); |
用moveToThread来处理移动到新的线程。
其他的非重要的函数就不一一列出,但是别忘记在断开连接的槽中释放连接:
01 |
void MyTcpServer::setData(const QByteArray &data, const int handle) |
03 |
emit sentData(data,handle); |
06 |
void MyTcpServer::sockDisConnectSlot(int handle, QString ip, quint16 prot) |
08 |
qDebug() <<"MyTcpServer::sockDisConnectSlot thread is:" << QThread::currentThreadId(); |
10 |
myTcpSocket * tcp = tcpClient->value(handle); |
11 |
tcpClient->remove(handle);//连接管理中移除断开连接的socket |
12 |
delete tcp;//释放断开连接的资源、、子对象线程也会释放 |
13 |
emit sockDisConnect(handle,ip,prot); |
其次就是继承的QTcpSocket的声明,直接上代码把:
01 |
//继承QTcpSocket以处理接收到的数据 |
02 |
class myTcpSocket : public QTcpSocket |
06 |
explicit myTcpSocket(qintptr socketDescriptor,QObject *parent = 0); |
09 |
void readData(const int,const QString &,constquint16,const QByteArray &);//发送获得用户发过来的数据 |
10 |
void sockDisConnect(const int ,const QString &,const quint16 );//断开连接的用户信息 |
12 |
void thisReadData();//处理接收到的数据 |
13 |
void sentData(const QByteArray & ,const int);//发送信号的槽 |
15 |
qintptr socketID;//保存id,== this->socketDescriptor();但是this->socketDescriptor()客户端断开会被释放, |
16 |
//断开信号用this->socketDescriptor()得不到正确值 |
这个实现其实更简单了、、、就把主要的信号槽连接部分附上把,还有发送数据需要注意下,我是用的Qt,其中信号槽用的新语法,而且配合的C++11的lambda表达式,你如果不清楚C++11,你最好先去看下C++11的文档。
01 |
myTcpSocket::myTcpSocket(qintptr socketDescriptor, QObject *parent) : |
02 |
QTcpSocket(parent),socketID(socketDescriptor) |
04 |
this->setSocketDescriptor(socketDescriptor); |
05 |
//转换系统信号到我们需要发送的数据、、直接用lambda表达式了,qdebug是输出线程信息 |
06 |
connect(this,&myTcpSocket::readyRead,this,&myTcpSocket::thisReadData); //连接到收到数据的处理函数 |
07 |
connect(this,&myTcpSocket::readyRead, //转换收到的信息,发送信号 |
09 |
qDebug() <<"myTcpSocket::myTcpSocket lambda readData thread is:" << QThread::currentThreadId(); emit readData(socketID,this->peerAddress().toString(),this->peerPort() ,this->readAll());//发送用户发过来的数据 |
11 |
connect(this,&myTcpSocket::disconnected, //断开连接的信号转换 |
13 |
qDebug() <<"myTcpSocket::myTcpSocket lambda sockDisConnect thread is:" << QThread::currentThreadId(); emit sockDisConnect(socketID,this->peerAddress().toString(),this->peerPort());//发送断开连接的用户信息 |
16 |
qDebug() << this->socketDescriptor() << " " << this->peerAddress().toString() |
17 |
<< " " << this->peerPort() << "myTcpSocket::myTcpSocket thread is " <<QThread::currentThreadId(); |
20 |
void myTcpSocket::sentData(const QByteArray &data, const int id) |
22 |
//如果是服务器判断好,直接调用write会出现跨线程的现象,所以服务器用广播,每个连接判断是否是自己要发送的信息。 |
23 |
if(id == socketID)//判断是否是此socket的信息 |
25 |
qDebug() << "myTcpSocket::sentData" << QThread::currentThreadId(); |
整篇代码中出现了n个qDebug() << ,这个是我为了查看运行所在的线程所设,实际应用中这些都没用的、、你自己删除把、、自己测试的例子和源码我还是保留了、、毕竟时间长了都忘得,留着那天一看就一目了然的、、
这个每个连接分配一个线程,连接太多很耗资源的、、您可以自己更改下,把多个连接移到一个线程,但是那样,你需要保存线程信息,更要小心线程的分配和释放时、、可以自己做下、、我也欢迎大家来探讨、、
最新更新:添加线程管理类(应该算个线程池),单例类。可预先设置线程数或者每个线程处理多少连接。原来的代码主要变动在新建断开连接处更新了、、详细请见代码。
全部的代码测试例子,服务端和客户端,我传到了我的github上了,附上项目地址:https://github.com/dushibaiyu/QtTcpThreadServer
转自:https://www.dushibaiyu.com/2013/12/qtcpserver%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E7%8E%B0.html
http://www.qtcn.org/bbs/read-htm-tid-60285.html
jerry该作者写的还是不错的,可惜需要qt5以上,使用qt4.8的话还是自己理解透qtcpserver、qttcpsocket、信号槽、movetothread后自己写一个吧。稍后会补上本人写的qt4.8代码。
- Qt编程可不可以结合其他的第三方库和本土API?(有zeroMQ的Qt封装,还可轻易使用Python的库)
作者:渡世白玉链接:http://www.zhihu.com/question/29030777/answer/59378712来源:知乎著作权归作者所有,转载请联系作者获得授权. 可以,十分可以,你 ...
- Qt+ECharts开发笔记(四):ECharts的饼图介绍、基础使用和Qt封装百分比图Demo
前言 前一篇介绍了横向柱图图.本篇将介绍基础饼图使用,并将其封装一层Qt. 本篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口. Demo演示 ...
- Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo
前言 上一篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口. 本篇的demo实现了自动排序的柱状图,实现了一个自动排序柱状图的基本交互方式,即Qt ...
- Qt封装QTcpServer参考资料--QT4中构建多线程的服务器
首先说一下对多线程这个名词的理解过程.以前听说过很多次多线程这个词,而且往往与服务器联系起来,因此一直把多线程误解为服务器特有的功能:直到这次课程设计,仔细学习了一下多线程的机制,才知道真正的意思.简 ...
- Qt封装QTcpServer参考资料--QT自带QTcpServer架构分析
Qt 4.6自带的threaddedfortuneserver是个简单明了的 Qt C/S网络编程server端程序的例子, 该例子演示了 QTcpServer与QThread配合的方法. 代码不多 ...
- Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo
前言 上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍.基础使用,并将其封装一层Qt. 本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt ...
- Qt封装百度人脸识别+图像识别
AI技术的发展在最近几年如火如荼,工资待遇也是水涨船高,应用的前景也是非常广阔,去年火起来的人脸识别,今年全国遍地开花,之前封装了下face++的人脸识别等接口,今年看了下百度的AI,还免费了,效果也 ...
- 66.QT-线程并发、QTcpServer并发、QThreadPool线程池
1.线程并发一个程序内部能拥有多个线程并行执行.一个线程的执行可以被认为是一个CPU在执行该程序.当一个程序运行在多线程下,就好像有多个CPU在同时执行该程序.总之,多线程即可以这么理解:多线程是处理 ...
- Qt中的多线程编程
http://www.ibm.com/developerworks/cn/linux/l-qt-mthrd/ Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功 ...
随机推荐
- Java常考面试题(四)
序言 双十一那天去我女朋友那了,11.11到11.13 漏了两天的内容,今天中午刚回来的,赶紧补一下前两天漏下的博文,我相信,坚持下来,会有成长的,等到出去实习那一天,肯定会很有帮助,会感谢现在自己的 ...
- 基于Xilinx Zynq Z7045 SoC的CNN的视觉识别应用
基于Xilinx Zynq Z7045 SoC的CNN的视觉识别应用 由 judyzhong 于 星期三, 08/16/2017 - 14:56 发表 作者:stark 近些年来随着科学技术的不断进步 ...
- [svc]linux文件权限
linux中,每个文件拥有三种权限 f dir权限位最佳实战 权限 对文件的影响 对文件夹的影响 r 可读取/阅读文件的内容 可以列出目录内容,无法cd,ls -l看到文件名,属性是乱码. w 可修改 ...
- 微网站|手机端html弹窗、弹层、提示框、加载条
layer mobile是为移动设备(手机.平板等webkit内核浏览器/webview)量身定做的弹层支撑,采用Native JavaScript编写,完全独立于PC版的layer,您需要按照场景选 ...
- 每日英语:Booming Tech Sector Redraws the Map
China's high-tech companies have made their mark on the nation's economy. Now, with growing cash and ...
- git(8):常用命令
Git常用操作命令收集: 1) 远程仓库相关命令 检出仓库:$ git clone git://github.com/jquery/jquery.git 查看远程仓库:$ git remote -v ...
- 【Fiddler】Fiddler抓包
简述 在调试H5页面.APP的网络请求时,常用Fiddler进行抓包,查看网络报文是否如我们设想.当然,Fiddler也可对我们电脑端发出HTTP请求进行抓包. Fiddler是以代理的形式运行的,即 ...
- 【架构】MVC模式
架构模式 如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architectural pattern),属于编程的方法论. MVC模式就是架构模式的一种,它不仅适用于 ...
- 【Acm】算法之美—Anagrams by Stack
题目概述:Anagrams by Stack How can anagrams result from sequences of stack operations? There are two seq ...
- 设计模式之装饰模式(iOS开发,代码用Objective-C展示)
在面向对象编程中有个重要的原则,里氏代换原则:一个软件实体如果使用的是一个父类的话,那么一定适用其子类,而且它察觉不出父类对象与子类对象的区别.也就是说,在软件设计里面,把父类替换成它的子类,程序的行 ...