QT控制台TCP通信,为每个客户端分配一个独立的线程(重写incomingConnection()函数)

QTcpServer若为每个客户端分配一个独立线程,必须重写incomingConnection()函数。

 

为何不直接把nextPendingConnection()函数返回的socket指针用在线程中

        在该函数的帮助中,QT明确表示:不能在线程中调用QTcpServer自动创建的QTcpSocket对象。并且在incomingConnection()的帮助中有提到,如若将客户端的连接传入单独的线程,则QTcpSocket对象必须创建在线程中,socket对象的创建通过重写incomingConnection()函数实现。

        QTcpServer类的工作机制是在有传入连接时,QTcpServer会创建一个与客户端匹配的socket,并返回一个指向socket内存的socketDescriptor(socket描述符),在QT中该描述符是qintptr类型的。然后QTcpSer​​​​​​​ver会自动调用incomingConnection()函数,该函数接收这个socketDescriptor。        

        在incomingConnection()函数的原实现中,创建了一个QTcpSocket对象,然后调用函数QTcpSocket::setSocketDescriptor(qintptr socketDescriptor)设置socket描述符。最后在incomingConnection()函数中又调用了addPendingConnection(QTcpSocket * socket),将创建的QTcpSocket对象指针挂起在已创建列表中,该行为可终止waitForNewConnection()的阻塞,并且用户可以通过调用nextPendingConnection()函数获得这个QTcpSocket对象指针。在线程版的incomingConnection()函数中,可以省略这步addPendingConnection()的调用,因为不再需要通过nextPendingConnection()函数来获得socket指针了。

重写incomingConnection()函数

        根据上面QTcpServer类的工作机制,我们尝试实现一个用于多线程的incomingConnection()函数。

        重写函数需要创建一个QTcpServer类的派生类,另外还需创建一个线程类。

代码:

服务端:

#include <iostream>
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QTime>
#include <QThread>
using namespace std;

void Wait(unsigned int T)//延迟暂停,参数毫秒
{
    QTime wait;//用于延迟
    wait.start();
    while ((unsigned int)wait.elapsed() < T)
        QCoreApplication::processEvents();
}

//每一个新的客户端连接,都会由QTcpServer通过重写的incomingConnection()自动分配一个新的线程。
//线程只接受了一个QTcpServer提供的socketDescriptor。
//QTcpSocket类的setSocketDescriptor()函数接收这个socketDescriptor,并利用它完成与客户端的连接。
class QTcpThread : public QThread
{
    QTcpSocket * socket;
    qintptr socketDescriptor;
public:
    QTcpThread(qintptr s) : QThread()
    {
        socketDescriptor = s;
    }
    void run() //重写QThread虚函数 run()
    {
        struct Stu{
            char ar[8];
            int b;
        };
        Stu stu1;
        QTcpSocket * socket_ = new QTcpSocket;
        socket = socket_;
        socket->setSocketDescriptor(socketDescriptor);
        while(socket->state()&&QAbstractSocket::ConnectedState)
        {
            if (socket->waitForReadyRead(1000)){//如果有新数据可供阅读,则立即返回true,否则错误或超出最大延迟则返回false
                socket->read((char*)&stu1,sizeof(Stu));  //接收一个结构体,以二进制
                cout << stu1.ar << "    " << stu1.b << endl;
            }
        }
    }
    ~QTcpThread()
    {
        socket->close();
        delete socket;
    }
};

//重写incomingConnection(),用于在线程中使用socket。
//对于重写incomingConnection()函数,QTcpServer的原则是,必须满足:该函数执行完之后,已创建了一个QTcpSocket对象,并且已设置好
//了其形参中传入的socket描述符“socketDescriptor”。
class NewQTcpServer : public QTcpServer
{
    //链表 用于释放QTcpThread对象内存
    struct Dlt{
        QTcpThread * thread_dlt;
        Dlt * next;
    };
    Dlt * dlt_ = 0;
public:
    NewQTcpServer(QObject * parent = Q_NULLPTR) : QTcpServer(parent){}

protected:
    void incomingConnection(qintptr socketDescriptor)
    {
        //将socketDescriptor传入新创建的线程对象,线程启动后QTcpSocket会使用socketDescriptor
        QTcpThread * thread = new QTcpThread(socketDescriptor);

        //保存新创建的线程对象指针到链表
        Dlt * dlt = new Dlt;
        dlt->thread_dlt = thread;
        dlt->next = dlt_;
        dlt_ = dlt;

        //启动线程 自动会执行run()函数
        thread->start();
    }
public:
    ~NewQTcpServer()
    {
        Dlt * d;
        while (dlt_)
        {
            d = dlt_->next;
            delete dlt_;
            dlt_ = d;
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    cout << "Server side!!" << endl;

    NewQTcpServer server;
    server.listen(QHostAddress::Any,50002);

    Wait(1000000);

    cout << "Connection close" << endl;
    server.close();
    getchar();

    return a.exec();
}

客户端:

#include <iostream>

#include <QCoreApplication>

#include <QTcpSocket>
#include <QHostAddress>

#include <QTime>//设置延迟函数和计算执行时间

using namespace std;
void Wait(unsigned int T)//延迟暂停,参数毫秒
{
    QTime wait;//用于延迟
    wait.start();
    while ((unsigned int)wait.elapsed() < T)
        QCoreApplication::processEvents();
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    cout << "Client side!!" << endl;
    QTcpSocket socket1;
    struct Stu{
        char ar[8];
        int b;
    };
    Stu stu={"zw",24};

    socket1.connectToHost(QHostAddress("192.168.100.101"),50002);//设置连接信息,并尝试连接
    qDebug() <<" 进入连接等待,最长5秒..." << endl;
    if (socket1.waitForConnected(5000))//连接等待,如果connectToHost连接成功,则立即返回true,否则到达最延迟后返回false
        qDebug() << "已连接!" << endl;
    else{
        qDebug() << "连接失败!" << endl;
        socket1.close();
        return a.exec();
        }
    while(socket1.state()&&QAbstractSocket::ConnectedState)
    {
        socket1.write((char*)&stu,sizeof(Stu));  //发送一个结构体,以二进制格式发送
        socket1.waitForBytesWritten();//发送等待,只要write发送出第一个字符时,立即返回true,否则到达最大延迟后返回false
        Wait(3000);
    }
    socket1.close();

    return a.exec();
}

运行结果截图:

  • 7
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt中,可以通过QTcpServer类来实现TCP服务器的编写,而多线程则可以通过QThread类来实现。下面是一个简单的示例程序,可以实现多线程与多个客户端通信: ```cpp #include <QtNetwork> #include <QtWidgets> #include <QtCore> class ClientThread : public QThread { Q_OBJECT public: explicit ClientThread(qintptr socketDescriptor, QObject *parent = nullptr) : QThread(parent), m_socketDescriptor(socketDescriptor) { } signals: void error(QTcpSocket::SocketError socketError); protected: void run() override { QTcpSocket socket; if (!socket.setSocketDescriptor(m_socketDescriptor)) { emit error(socket.error()); return; } connect(&socket, &QTcpSocket::readyRead, this, &ClientThread::readyRead); connect(&socket, &QTcpSocket::disconnected, this, &ClientThread::disconnected); exec(); } private slots: void readyRead() { QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender()); if (!socket) return; QByteArray data = socket->readAll(); // 处理接收到的数据 socket->flush(); } void disconnected() { QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender()); if (!socket) return; socket->deleteLater(); quit(); } private: qintptr m_socketDescriptor; }; class TcpServer : public QTcpServer { Q_OBJECT public: TcpServer(QObject *parent = nullptr) : QTcpServer(parent) { } protected: void incomingConnection(qintptr socketDescriptor) override { ClientThread *thread = new ClientThread(socketDescriptor, this); connect(thread, &ClientThread::finished, thread, &ClientThread::deleteLater); thread->start(); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); TcpServer server; if (!server.listen(QHostAddress::Any, 1234)) { qCritical() << "Failed to start server:" << server.errorString(); return 1; } qDebug() << "Server started:" << server.serverAddress().toString() << server.serverPort(); return app.exec(); } #include "main.moc" ``` 在这个示例程序中,TcpServer类继承自QTcpServer类,其中的incomingConnection()函数会在新的客户端连接时被调用。在该函数中,我们创建一个新的ClientThread线程,并将客户端的socket描述符传递给它。在ClientThread线程中,我们可以通过QTcpSocket类来与客户端进行通信。当客户端连接断开时,我们需要清理socket并退出线程。 需要注意的是,由于Qt的对象树模型,我们需要在ClientThread线程中使用deleteLater()函数来删除socket对象。这可以确保socket对象不会在其所属的线程销毁之前被销毁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值