QT-TCP服务端开启多个线程处理收到多客户端发来的消息示例
本文主要解决的问题是:
二、一个服务端怎么开启多个线程来处理所有客户端发来的消息?(换汤不换药,可以看这篇博客Qt封装一个类管理moveToThread( )正确的开启多线程、安全的退出线程的实例不管是开启多个客户端还是一个服务端开启多个线程都可以用此方法)这里我们还需要考虑弄一个全局的缓存区暂时来存放从客户端收到的消息,因为有时候服务端对消息的处理速度慢时,可能会造成数据丢失这些原因。
一些用到的函数等解释:
void incomingConnection ( int socketDescriptor ) | 当有新连接可用时,QTcpServer将调用此虚拟函数。注意:如果希望将传入连接作为另一个线程中的新QTcpSocket对象处理,则必须将socketDescriptor传递给另一个线程,在那里创建QTcpSocket对象并使用其setSocketDescriptor()方法。 |
一、一个服务端怎么处理多客户端的连接?
首先要知道在QT中有QTcpServer来进行TCP通信,每个客户端连接到服务端时,这个QTcpSever会给他们分配一个socket。
伪代码:
/************ 1 创建一个QTcpSever 2 监听端口 3 QTcpSever有客户端连接上来是,会触发newConnection信号,或者incomingConnection虚函数 4 创建一个QList来存储QTcpSocket,后续需要用到每个客户端对应的socket时,在这个容器里取出就行了。 ************/
1 在这里我用到了重写虚函数incomingConnection继承QTcpServer的方法来实现一个服务端如何处理多个客户端的连接,
2 当然也可以不用这种方法,直接在触发readReady信号时遍历容器即可,可以看本篇博客QT TCP服务端处理多个客户端发来的消息
2的这种方法比较容易看懂,但是1也很简单,现在我们来看1的方法怎么实现吧。
在这里我把QTcpSever和QTcpSocket的处理封装成两个类,比较有MVC的那种分层管理的想法;当然你也可以不用封装成两个类,封装成一个也行。
调用代码示例:如此我们便开启了一个服务器了
SeverByTcp m_tcpSever = new SeverByTcp(this); //开启tcp监听 if(!m_tcpSever->startSeverListen(sTcpPort)) { return false; }
下面时示例的详细代码,每一步都有说明。
A.继承QTcpServer的类SeverByTcp
.h
#ifndef SEVERBYTCP_H#define SEVERBYTCP_H#include #include #include #include "tcpclientsocket.h"//这个类是自己继承QTcpSocket的,下个示例就会补充class SeverByTcp : public QTcpServer{ Q_OBJECTpublic: explicit SeverByTcp(QObject *parent = 0); //开启监听 bool startSeverListen(const QString sHostPort); // 当有新连接可用时,QTcpServer将调用此虚拟函数 void incomingConnection(int socketDescriptor); signals: public slots: //处理服务器收到tcpsocket发过来的信息 void slotUpdateServer(QString, int); //告诉服务器有客户端断开连接 void slotClientDisconnected(int);private: // 连接到服务器的客户端链表 QList m_tcpClientSocketList; };#endif // SEVERBYTCP_H
.cpp
#include "severbytcp.h"SeverByTcp::SeverByTcp(QObject *parent) : QTcpServer(parent){ }bool SeverByTcp::startSeverListen(const QString sHostPort){ //监听端口 if(listen(QHostAddress::Any, sHostPort.toInt())) { return true; } return false;}void SeverByTcp::incomingConnection(int socketDescriptor){ //只要有新的连接就生成一个新的通信套接字 TcpClientSocket *tcpClientSocket = new TcpClientSocket(this); //将新创建的通信套接字描述符指定为参数socketdescriptor tcpClientSocket->setSocketDescriptor(socketDescriptor); //将这个套接字加入客户端套接字列表中 m_tcpClientSocketList.append(tcpClientSocket); //接收到tcpclientsocket发送过来信息 connect(tcpClientSocket,SIGNAL(sigUpdateSever(QString,int)),this,SLOT(slotUpdateServer(QString, int))); //处理客户端掉线 connect(tcpClientSocket,SIGNAL(sigClientDisconnect(int)),this,SLOT(slotClientDisconnected(int)));}void SeverByTcp::slotUpdateServer(QString sMsg, int iLength){ //这里的sMsg就是服务端从客户端收到的消息了 //可以在这里进行对消息的处理,你可以按照自己的需求来设计}void SeverByTcp::slotClientDisconnected(int iSocketDescriptor){ //如果有客户端断开连接, 就将列表中的套接字删除 for(int i = 0; i socketDescriptor() == iSocketDescriptor) { m_tcpClientSocketList.removeAt(i); return; } } return;}
B.继承QTcpSocket的类TcpClientSocket
.h
#ifndef TCPCLIENTSOCKET_H#define TCPCLIENTSOCKET_H#include #include class TcpClientSocket : public QTcpSocket{ Q_OBJECTpublic: explicit TcpClientSocket(QObject *parent = 0);signals: // 通知服务器 客户端收到了消息 void sigUpdateSever(QString,int); // 通知服务器 客户端掉线 void sigClientDisconnect(int);public slots: // 处理readyRead信号读取数据 void slotReceiveData(); // 用来处理客户端断开连接触发disconnected信号 void slotClientDisconnected();private: };#endif // TCPCLIENTSOCKET_H
.cpp
#include "tcpclientsocket.h"TcpClientSocket::TcpClientSocket(QObject *parent){ //客户端有消息传进来时会触发信号readyRead() connect(this,SIGNAL(readyRead()),this,SLOT(slotReceiveData())); //客户端断开连接时会触发信号disconnected() connect(this,SIGNAL(disconnected()),this,SLOT(slotClientDisconnected()));}void TcpClientSocket::slotReceiveData(){int iMaxLength = 1024; //QByteArray baArray = readAll();//把socket中的消息读出 QByteArray baArray = read(iMaxLength);设置长度大小是为了考虑粘包的问题,这里每次读出固定的长度即可 QString sMsg = baArray; //通知QTcpSever socket收到客户端发来的消息了 emit sigUpdateSever(sMsg, iMaxLength);}void TcpClientSocket::slotClientDisconnected(){ //通知服务器有客户端掉线 emit sigClientDisconnect(this->socketDescriptor());}