QTcpServer多线程实现
2016-08-20 16:58
363 查看
转自:http://www.dushibaiyu.com/2013/12/qtcpserver%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E7%8E%B0.html
实现时分别继承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的自己的类声明,代码注释个人以为挺详细了:
接着是我们重写void QTcpServer::incomingConnection(qintptr socketDescriptor)的实现:
用moveToThread来处理移动到新的线程。
其他的非重要的函数就不一一列出,但是别忘记在断开连接的槽中释放连接:
其次就是继承的QTcpSocket的声明,直接上代码把:
这个实现其实更简单了、、、就把主要的信号槽连接部分附上把,还有发送数据需要注意下,我是用的Qt,其中信号槽用的新语法,而且配合的C++11的lambda表达式,你如果不清楚C++11,你最好先去看下C++11的文档。
整篇代码中出现了n个qDebug() <<,这个是我为了查看运行所在的线程所设,实际应用中这些都没用的、、你自己删除把、、自己测试的例子和源码我还是保留了、、毕竟时间长了都忘得,留着那天一看就一目了然的、、
这个每个连接分配一个线程,连接太多很耗资源的、、您可以自己更改下,把多个连接移到一个线程,但是那样,你需要保存线程信息,更要小心线程的分配和释放时、、可以自己做下、、我也欢迎大家来探讨、、
最新更新:添加线程管理类(应该算个线程池),单例类。可预先设置线程数或者每个线程处理多少连接。原来的代码主要变动在新建断开连接处更新了、、详细请见代码。
全部的代码测试例子,服务端和客户端,我传到了我的github上了,附上项目地址:https://github.com/dushibaiyu/QtTcpThreadServer
实现时分别继承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 |
03 | { |
04 | Q_OBJECT |
05 | public : |
06 | explicit MyTcpServer(QObject *parent =0); |
07 | ~MyTcpServer(); |
08 | signals: |
09 | void connectClient( const int , const QString & , const quint16 ); //发送新用户连接信息 |
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发送消息 |
13 | public slots: |
14 | void setData( const QByteArray const int handle); //想用户发送消息 |
15 | void readDataSlot( const int , const QString &, const quint16, const QByteArray &); //发送获得用户发过来的数据 |
16 | void sockDisConnectSlot( int handle, //断开连接的用户信息 |
17 |
18 | protected : |
19 | void incomingConnection(qintptr socketDescriptor); //覆盖已获取多线程 |
20 | private : |
21 | QMap< int ,myTcpSocket *> * tcpClient; |
22 | }; |
01 | void MyTcpServer::incomingConnection(qintptr socketDescriptor) |
02 | { |
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(); //线程开始运行 |
12 |
13 | tcpClient->insert(socketDescriptor,tcpTemp); //插入到连接信息中 |
14 | qDebug() << "incomingConnection THREAD IS:" <<QThread::currentThreadId(); |
15 | //发送连接信号 |
16 | emit connectClient(tcpTemp->socketDescriptor(),tcpTemp->peerAddress().toString(),tcpTemp->peerPort()); |
17 |
18 | } |
其他的非重要的函数就不一一列出,但是别忘记在断开连接的槽中释放连接:
01 | void MyTcpServer::setData( const QByteArray const int handle) |
02 | { |
03 | emit sentData(data,handle); |
04 | } |
05 |
06 | void MyTcpServer::sockDisConnectSlot( int handle, |
07 | { |
08 | qDebug() << "MyTcpServer::sockDisConnectSlot thread is:" << QThread::currentThreadId(); |
09 |
10 | myTcpSocket |
11 | tcpClient-> remove (handle); //连接管理中移除断开连接的socket |
12 | delete tcp; //释放断开连接的资源、、子对象线程也会释放 |
13 | emit sockDisConnect(handle,ip,prot); |
14 | } |
01 | //继承QTcpSocket以处理接收到的数据 |
02 | class myTcpSocket : public QTcpSocket |
03 | { |
04 | Q_OBJECT |
05 | public : |
06 | explicit myTcpSocket(qintptr |
07 |
08 | signals: |
09 | void readData( const int , const QString &, const quint16, const QByteArray &); //发送获得用户发过来的数据 |
10 | void sockDisConnect( const int , const QString &, const quint16 ); //断开连接的用户信息 |
11 | public slots: |
12 | void thisReadData(); //处理接收到的数据 |
13 | void sentData( const QByteArray & , const int ); //发送信号的槽 |
14 | private : |
15 | qintptr socketID; //保存id,== this->socketDescriptor();但是this->socketDescriptor()客户端断开会被释放, |
16 | //断开信号用this->socketDescriptor()得不到正确值 |
17 | }; |
01 | myTcpSocket::myTcpSocket(qintptr |
02 | QTcpSocket(parent),socketID(socketDescriptor) |
03 | { |
04 | this ->setSocketDescriptor(socketDescriptor); |
05 | //转换系统信号到我们需要发送的数据、、直接用lambda表达式了,qdebug是输出线程信息 |
06 | connect( this ,&myTcpSocket::readyRead, this ,&myTcpSocket::thisReadData); //连接到收到数据的处理函数 |
07 | connect( this ,&myTcpSocket::readyRead, //转换收到的信息,发送信号 |
08 | [ this ](){ |
09 | qDebug() << "myTcpSocket::myTcpSocket lambda readData thread is:" << QThread::currentThreadId(); emit readData(socketID, this ->peerAddress().toString(), this ->peerPort() , this ->readAll()); //发送用户发过来的数据 |
10 | }); |
11 | connect( this ,&myTcpSocket::disconnected, //断开连接的信号转换 |
12 | [ this ](){ |
13 | qDebug() << "myTcpSocket::myTcpSocket lambda sockDisConnect thread is:" << QThread::currentThreadId(); emit sockDisConnect(socketID, this ->peerAddress().toString(), this ->peerPort()); //发送断开连接的用户信息 |
14 | }); |
15 |
16 | qDebug() << this ->socketDescriptor() << " " << this ->peerAddress().toString() |
17 | << " " << this ->peerPort() "myTcpSocket::myTcpSocket thread is " <<QThread::currentThreadId(); |
18 | } |
19 |
20 | void myTcpSocket::sentData( const QByteArray const int id) |
21 | { |
22 | //如果是服务器判断好,直接调用write会出现跨线程的现象,所以服务器用广播,每个连接判断是否是自己要发送的信息。 |
23 | if (id //判断是否是此socket的信息 |
24 | { |
25 | qDebug() << "myTcpSocket::sentData" << QThread::currentThreadId(); |
26 | write(data); |
27 | } |
28 | } |
这个每个连接分配一个线程,连接太多很耗资源的、、您可以自己更改下,把多个连接移到一个线程,但是那样,你需要保存线程信息,更要小心线程的分配和释放时、、可以自己做下、、我也欢迎大家来探讨、、
最新更新:添加线程管理类(应该算个线程池),单例类。可预先设置线程数或者每个线程处理多少连接。原来的代码主要变动在新建断开连接处更新了、、详细请见代码。
全部的代码测试例子,服务端和客户端,我传到了我的github上了,附上项目地址:https://github.com/dushibaiyu/QtTcpThreadServer
相关文章推荐
- QTcpServer多线程实现
- QTcpServer多线程实现
- QTcpServer多线程实现
- 多线程的QTcpServer
- QTcpServer多线程资源释放
- 一个非常有趣的QTcpServer多线程编程问题
- Qt下应用QTcpServer与QTcpSocket实现Tcp控制
- 新手易步入的Qt中QTcpServer多线程误区
- 创建一个多线程的QTcpServer
- QTcpSocket和QTcpServer实现聊天小工具
- QT:Qsocket长连接的实现(多线程服务器)
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- Qt QTcpServer 坑爹 to death!
- Qt中采用多线程实现Socket编程
- Qt中采用多线程实现Socket编程-跨线程调用问题
- qt4 tcp QTcpSocket QTcpServer 传输文件
- Qt之QTcpServer/QTcpSocket简单收发信息(2)
- Qt 在程序启动的时候用多线程实现播放gif动画
- 基于QT4的一个多线程工程实现
- QTcpSocket QTcpServer 类的使用方法