当前位置:   article > 正文

QT学习:QTcpServer多线程实现_qt tcp多线程

qt tcp多线程

QTcpServer多线程实现

目的:每个客户端连接的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的自己的类声明,代码注释个人以为挺详细了:

  1. //继承QTCPSERVER以实现多线程TCPscoket的服务器。
  2. class MyTcpServer : public QTcpServer
  3. {
  4. Q_OBJECT
  5. public:
  6. explicit MyTcpServer(QObject *parent = 0);
  7. ~MyTcpServer();
  8. signals:
  9. 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 & data, const int handle);//想用户发送消息
  15. void readDataSlot(const int, const QString &, const quint16,const QByteArray &);//发送获得用户发过来的数据
  16. void sockDisConnectSlot(int handle, QString ip, quint16 prot);//断开连接的用户信息
  17. protected:
  18. void incomingConnection(qintptr socketDescriptor);//覆盖已获取多线程
  19. private:
  20. QMap<int,myTcpSocket *> * tcpClient;
  21. };

接着是我们重写void QTcpServer::incomingConnection(qintptr socketDescriptor)的实现:

  1. void MyTcpServer::incomingConnection(qintptr socketDescriptor)
  2. {
  3. myTcpSocket * tcpTemp = new myTcpSocket(socketDescriptor);
  4. QThread * thread = new QThread(tcpTemp);//把线程的父类设为连接的,防止内存泄漏
  5. //可以信号连接信号的,我要捕捉线程ID就独立出来函数了,使用中还是直接连接信号效率应该有优势
  6. connect(tcpTemp,&myTcpSocket::readData,this,&MyTcpServer::readDataSlot);//接受到数据
  7. connect(tcpTemp,&myTcpSocket::sockDisConnect,this,&MyTcpServer::sockDisConnectSlot);//断开连接的处理,从列表移除,并释放断开的Tcpsocket
  8. connect(this,&MyTcpServer::sentData,tcpTemp,&myTcpSocket::sentData);//发送数据
  9. connect(tcpTemp,&myTcpSocket::disconnected,thread,&QThread::quit);//断开连接时线程退出
  10. tcpTemp->moveToThread(thread);//把tcp类移动到新的线程
  11. thread->start();//线程开始运行
  12. tcpClient->insert(socketDescriptor,tcpTemp);//插入到连接信息中
  13. qDebug() <<"incomingConnection THREAD IS:" <<QThread::currentThreadId();
  14. //发送连接信号
  15. emit connectClient(tcpTemp->socketDescriptor(),tcpTemp->peerAddress().toString(),tcpTemp->peerPort());
  16. }

 

用moveToThread来处理移动到新的线程。

其他的非重要的函数就不一一列出,但是别忘记在断开连接的槽中释放连接:

  1. void MyTcpServer::setData(const QByteArray &data, const int handle)
  2. {
  3. emit sentData(data,handle);
  4. }
  5. void MyTcpServer::sockDisConnectSlot(int handle, QString ip, quint16 prot)
  6. {
  7. qDebug() <<"MyTcpServer::sockDisConnectSlot thread is:" << QThread::currentThreadId();
  8. myTcpSocket * tcp = tcpClient->value(handle);
  9. tcpClient->remove(handle);//连接管理中移除断开连接的socket
  10. delete tcp;//释放断开连接的资源、、子对象线程也会释放
  11. emit sockDisConnect(handle,ip,prot);
  12. }

其次就是继承的QTcpSocket的声明,直接上代码把:

  1. //继承QTcpSocket以处理接收到的数据
  2. class myTcpSocket : public QTcpSocket
  3. {
  4. Q_OBJECT
  5. public:
  6. explicit myTcpSocket(qintptr socketDescriptor,QObject *parent = 0);
  7. signals:
  8. void readData(const int,const QString &,const quint16,const QByteArray &);//发送获得用户发过来的数据
  9. void sockDisConnect(const int ,const QString &,const quint16 );//断开连接的用户信息
  10. public slots:
  11. void thisReadData();//处理接收到的数据
  12. void sentData(const QByteArray & ,const int);//发送信号的槽
  13. private:
  14. qintptr socketID;//保存id,== this->socketDescriptor();但是this->socketDescriptor()客户端断开会被释放,
  15. //断开信号用this->socketDescriptor()得不到正确值
  16. };

这个实现其实更简单了、、、就把主要的信号槽连接部分附上把,还有发送数据需要注意下,我是用的Qt,其中信号槽用的新语法,而且配合的C++11的lambda表达式,你如果不清楚C++11,你最好先去看下C++11的文档。

  1. myTcpSocket::myTcpSocket(qintptr socketDescriptor, QObject *parent) :
  2. QTcpSocket(parent),socketID(socketDescriptor)
  3. {
  4. this->setSocketDescriptor(socketDescriptor);
  5. //转换系统信号到我们需要发送的数据、、直接用lambda表达式了,qdebug是输出线程信息
  6. connect(this,&myTcpSocket::readyRead,this,&myTcpSocket::thisReadData); //连接到收到数据的处理函数
  7. connect(this,&myTcpSocket::readyRead, //转换收到的信息,发送信号
  8. [this](){
  9. 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. qDebug() << this->socketDescriptor() << " " << this->peerAddress().toString()
  16. << " " << this->peerPort() << "myTcpSocket::myTcpSocket thread is " <<QThread::currentThreadId();
  17. }
  18. void myTcpSocket::sentData(const QByteArray &data, const int id)
  19. {
  20. //如果是服务器判断好,直接调用write会出现跨线程的现象,所以服务器用广播,每个连接判断是否是自己要发送的信息。
  21. if(id == socketID)//判断是否是此socket的信息
  22. {
  23. qDebug() << "myTcpSocket::sentData" << QThread::currentThreadId();
  24. write(data);
  25. }
  26. }

整篇代码中出现了n个qDebug() << ,这个是我为了查看运行所在的线程所设,实际应用中这些都没用的、、你自己删除把、、自己测试的例子和源码我还是保留了、、毕竟时间长了都忘得,留着那天一看就一目了然的、、

这个每个连接分配一个线程,连接太多很耗资源的、、您可以自己更改下,把多个连接移到一个线程,但是那样,你需要保存线程信息,更要小心线程的分配和释放时、、可以自己做下、、我也欢迎大家来探讨、、

最新更新:添加线程管理类(应该算个线程池),单例类。可预先设置线程数或者每个线程处理多少连接。原来的代码主要变动在新建断开连接处更新了、、详细请见代码。

转载于:https://www.dushibaiyu.com/2013/12/qtcpserver%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%9E%E7%8E%B0.html

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/187347?site
推荐阅读
相关标签
  

闽ICP备14008679号