QTcpServer多线程实现

来源:互联网 发布:淘宝手机端发布详情页 编辑:程序博客网 时间:2024/06/08 03:12

转自: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的自己的类声明,代码注释个人以为挺详细了:

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

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

01void 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}

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

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

01void MyTcpServer::setData(const QByteArray &data, const int handle)
02{
03    emit sentData(data,handle);
04}
05 
06void MyTcpServer::sockDisConnectSlot(int handle, QString ip, quint16 prot)
07{
08    qDebug() <<"MyTcpServer::sockDisConnectSlot thread is:" << QThread::currentThreadId();
09 
10    myTcpSocket * tcp = tcpClient->value(handle);
11    tcpClient->remove(handle);//连接管理中移除断开连接的socket
12    delete tcp;//释放断开连接的资源、、子对象线程也会释放
13    emit sockDisConnect(handle,ip,prot);
14}

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

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

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

01myTcpSocket::myTcpSocket(qintptr socketDescriptor, QObject *parent) :
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 
20void myTcpSocket::sentData(const QByteArray &data, const int id)
21{
22    //如果是服务器判断好,直接调用write会出现跨线程的现象,所以服务器用广播,每个连接判断是否是自己要发送的信息。
23    if(id == socketID)//判断是否是此socket的信息
24    {
25        qDebug() << "myTcpSocket::sentData" << QThread::currentThreadId();
26        write(data);
27    }
28}

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

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

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

全部的代码测试例子,服务端和客户端,我传到了我的github上了,附上项目地址:https://github.com/dushibaiyu/QtTcpThreadServer

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 美图m4手机开不开机怎么办 比亚迪m6冷凝器散热不好怎么办 深圳市小汽车摇号审核没通过怎么办 扫码开门售货机拿了不给钱怎么办 预付卡办完后对方不愿退款怎么办 海尔88u52显示內存不足怎么办 京东账号绑定的手机好不用了怎么办 京东账号换手机了手机号没变怎么办 京东发票的号和手机对不起来怎么办 京东账号忘记了只有身份证怎么办啊 京东账号手机号换了忘记账号怎么办 新换的卡被注册过京东号怎么办 以旧换新旧的没给商家报案怎么办 宜家家居家居指南地址写错怎么办 苏宁任性付没还遭到恐吓心意怎么办 大王卡激活后一直是E网怎么办 京东退货已取走后悔了怎么办 退差价把下单立返红包退还了怎么办 苏宁电器发票丢了换电器怎么办 苹果5s访问限制密码忘了怎么办 京东买的暴风电视出现问题了怎么办 控水一个月的三角梅还没开花怎么办 帮别人办手机分期不还怎么办 国美在线没有信用卡分期不了怎么办 手机店办理分期被老板套现了怎么办 美的空调保修卡丢了怎么办 格力空调保修卡丢了怎么办 荣耀9i手机总是滑手怎么办? 春兰空调没发票不给修怎么办 洗衣机顶盖的安全开关坏了怎么办 苹果手机的开关健坏了怎么办 淘宝买的东西快递弄破损了怎么办 京东购买邮来手机里面没有怎么办 孕期建卡病历本丢了怎么办 四维检查胎儿心脏有缺陷怎么办 七个月的宝宝俩个蛋蛋都疝气怎么办 电脑有些网站看视频不能全屏怎么办 小米滑板车坏了售后不保修怎么办 红米4a电池不耐用怎么办 华为手机买个别人退货的怎么办 京东购物怎么查订单查询不到怎么办