QT TCP多线程网络通信

学习目标: TCP网络通信编程

学习前置环境

运行环境:qt creator 4.12

QT TCP网络通信编程-CSDN博客

Qt 线程 QThread类详解-CSDN博客

学习内容

使用多线程技术实现服务端计数器

 核心代码

客户端

客户端:负责连接服务端,每次连接次数+1。以及连接的报错信息

#include "dialog.h"
#include "ui_dialog.h"
#include<QDebug>
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
    ui->server_ip->setText("127.0.0.1");
    ui->server_port->setText("8888");
    clientSocket =new QTcpSocket;
    setWindowTitle("连接计数器客户端");
    //连接错误回调
    QObject::connect(clientSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error)
                     , this, [this](QAbstractSocket::SocketError error){
        switch (error) {
        case QAbstractSocket::RemoteHostClosedError: // 远程主机关闭连接
           // QMessageBox::information(this, "提示", "远程主机关闭连接", QMessageBox::Yes);
            break;

        case QAbstractSocket::HostNotFoundError: // 找不到主机地址
            QMessageBox::information(this, "提示", "找不到主机地址", QMessageBox::Yes);
            break;

        case QAbstractSocket::ConnectionRefusedError: // 连接被对方拒绝(或者超时)
            QMessageBox::information(this, "提示", "连接被对方拒绝(或者超时)", QMessageBox::Yes);
            break;

        default:
            QMessageBox::information(this, "提示", tr("致命错误为:").arg(clientSocket->errorString()), QMessageBox::Yes);
        }
        ui->request->setEnabled(true);
        ui->close->setEnabled(true);
    });
    //当 socket 成功连接到服务器时,会发射 connected() 信号。
    connect(clientSocket,&QTcpSocket::connected,this,[this](){
        QString str ="已经连接到服务器端\n服务器端ip:"+clientSocket->peerAddress().toString()+"服务器端port:"+QString::number(clientSocket->peerPort());
        //QMessageBox::information(this, "提示",str , QMessageBox::Yes);
        ui->request->setEnabled(false);
        ui->close->setEnabled(true);


            QString msg=ui->currentv->text()+'\n';
            clientSocket->write(msg.toUtf8(),msg.length());
            int count =(ui->currentv->text().toUInt());
            ui->currentv->setNum(++count);


    });
    //当 socket 与服务器断开连接时,会发射 disconnected() 信号。
    connect(clientSocket,&QTcpSocket::disconnected,this,[this](){
        QString str ="已断开与服务器端的连接\n服务器端ip:"+clientSocket->peerAddress().toString()+"服务器端port:"+QString::number(clientSocket->peerPort());
        //QMessageBox::information(this, "提示",str , QMessageBox::Yes);


        clientSocket->close();


    });


    ui->request->setEnabled(true);
    ui->close->setEnabled(true);



}

Dialog::~Dialog()
{
    delete ui;
}


void Dialog::on_request_clicked()
{


    clientSocket->connectToHost(ui->server_ip->text(),ui->server_port->text().toInt());

}

void Dialog::on_close_clicked()
{
    clientSocket->close(); // 取消已有的连接  后续触发断开回调
    ui->request->setEnabled(true);
    ui->close->setEnabled(false);
}

服务端

新连接请求类

通过继承重写的方式,实现新连接的回调操作。当然你也可以使用信号槽机制。如

connect(tcpServer, &QTcpServer::newConnection,对象,行为)。

实现功能:交给线程池处理,绑定实现连接断开前,把公用计数器+1操作,释放并清理资源。可以理解为绑定亡语操作,死后(连接断开)触发。

#ifndef TCPNEWCONNET_H
#define TCPNEWCONNET_H

#include"writethread.h"
#include"dialog.h"


class Dialog;


class TcpNewConnet : public QTcpServer  //基于重写虚函数 实现新连接回调函数
{
Q_OBJECT
public:
    TcpNewConnet()=default;
    ~TcpNewConnet()=default;
    TcpNewConnet(QObject *parent=0)
        :QTcpServer(parent)
    {
        dlgs =(Dialog*)parent;

    }
protected:
    // 当有新连接的时候会自动调用此函数
  void TcpNewConnet::incomingConnection(qintptr socketdescriptor){
    WriteThread *thread=new WriteThread(socketdescriptor,0);

   // 此处用于处理对话框显示统计访问次数信息
   connect(thread,&QThread::finished,dlgs,&Dialog::slotsdispFunc);

   connect(thread,&QThread::finished,thread,&QThread::deleteLater);

   thread->start(); // 通过执行这条语句来调用run()函数
}


    Dialog *dlgs;
};

#endif // TCPNEWCONNET_H

多线程类

依然是通过重写的方式,注意点是Tcpsocket的生命周期,当过了{}作用域会自动释放这条连接。实现了客户端连接成功,再释放。

功能:创造一个连接,然后等这个连接死亡。触发亡语操作。

#include "writethread.h"




WriteThread::WriteThread(int socketdescriptor,QObject *parent)
    :QThread(parent),socketdescriptor(socketdescriptor)
{

}


void WriteThread::run(){ //多线程执行的函数
    //QTcpSocket* tcp =new QTcpSocket;    持久化连接
   {
        QTcpSocket tcp2; //离开作用域自动释放这条新连接
        QTcpSocket* tcp =& tcp2;
        if(!tcp->setSocketDescriptor(socketdescriptor)){
            emit myerror(tcp->error());  //触发自定义的error信号
            return;
        }
        qDebug()<<"run()";
        QByteArray data;
        QDataStream out(&data,QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_5_12);

        tcp->write(data);

    }
    //tcp->disconnectFromHost(); //主动断开与远程主机的TCP连接。
}

主逻辑类

主要实现按钮开启和关闭服务器

#include "dialog.h"
#include "ui_dialog.h"
#include<QMessageBox>
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
     setWindowTitle("连接计数器服务端");
    ui->server_ip->setText("127.0.0.1");
    ui->server_port->setText("8888");
    icount= 0;
    tcpserver=new TcpNewConnet(this);


}

Dialog::~Dialog()
{
    delete ui;
}


void Dialog::slotsdispFunc(){
    ui->currentv->setText(tr("客户端请求%1次").arg(++icount));
}

void Dialog::on_close_clicked()
{
        //先关闭所有socket
       if(!tcpserver){
           tcpserver->disconnect(); //用于断开 QTcpSocket 对象的所有信号与槽的连接。
           tcpserver->close();      //它会向对端发送 FIN 数据包,并等待对端的确认,完成 TCP 连接的正常关闭过程。
           //fin回调 已调用 tcpSocket->deleteLater(); //它不会立即删除对象,而是将其标记为待删除状态,等到当前事件循环结束后再执行删除操作。

       }
       if(tcpserver->isListening())
       {
           tcpserver->close();
            //不调用 deleteLater 为了下次再次开启
           ui->listen->setEnabled(true);
           ui->close->setEnabled(false);
           QMessageBox::critical(this,tr("提示"),
                                 tr("多线程服务器已关闭"));
       }


}

void Dialog::on_listen_clicked()
{
    QString ip(ui->server_ip->text());
    uint16_t port =ui->server_port->text().toUInt();
    if(!tcpserver->listen(QHostAddress(ip),port)){

        tcpserver->close();QMessageBox::critical(this,tr("提示"),
                                                 tr("多线程服务器已关闭"));
        return;
    }
    QMessageBox::information(this,tr("提示"),tr("多线程服务器已经启动"));
    ui->listen->setEnabled(false);
    ui->close->setEnabled(true);
}

总结

通过继承重写和信号槽的方式,可以实现连接建立,断开,发送前,发送后等等操作绑定,重写需要去找指定的重写函数,而信号去找指定的信号名。信号槽机制当绑定多个的时候,是按照绑定的顺序执行,因为底层是信号队列,保证顺序。

如果对信号槽有兴趣,可以看我之前发布的qt 多线程和网络编程文章。

最后附上源代码链接
对您有帮助的话,帮忙点个star

 41-clinet-count · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

41-server-count · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

### 回答1: QT是一个跨平台的应用程序框架,提供了丰富的库和工具,用于开发图形用户界面和网络通信等功能。在QT中,可以使用TCP协议进行多线程通信。 首先,我们需要创建一个TCPServer对象来监听客户端的连接请求。可以使用QT提供的QTcpServer类来实现,通过调用其listen()函数并指定监听的IP地址和端口号,可以将服务器设置为监听状态。 当有客户端连接到服务器时,QTcpServer会发射一个newConnection()信号。可以将该信号与我们自定义的槽函数进行关联,以便在有新的连接时进行处理。在槽函数中,可以通过调用nextPendingConnection()函数获得与客户端的套接字通信。 为了实现多线程通信,我们可以将每个新的连接分配给一个单独的线程进行处理。可以使用QT提供的QThread类来创建线程,并将服务器的套接字传递给线程对象。 在线程对象的run()函数中,可以使用QT提供的QTcpSocket类来进行数据的读取和写入。通过调用read()函数可以接收数据,通过调用write()函数可以发送数据。当然,在具体的应用中,可以根据需求进行数据的解析和处理。 为了保证多线程通信的安全性,我们可以使用QT提供的互斥量或QMutex类来进行同步操作。当多个线程同时访问共享资源时,可以使用互斥锁来保护这些资源,避免出现数据竞争的情况。 总之,QT提供了丰富的类和函数来实现TCP多线程通信。通过合理地利用QT网络编程和多线程编程的功能,我们可以轻松地实现高效的网络通信应用程序。 ### 回答2: Qt是一个跨平台的应用程序开发框架,提供了丰富的功能和工具来简化和加速应用程序的开发过程。其中,TCP多线程通信Qt中一种常见的应用场景。 在Qt中,可以使用QTcpSocket和QTcpServer类来实现TCP通信QTcpSocket类用于实现客户端的功能,可以连接到远程服务器并发送和接收数据。QTcpServer类用于实现服务器端的功能,可以监听和接受客户端的连接,并与客户端进行数据交互。 在多线程通信中,可以使用QtQThread类来创建多个线程,每个线程负责一个TCP连接。在每个线程中,可以创建一个QTcpSocket对象,用于与某个客户端进行通信。通过信号和槽机制,可以实现线程间的通信和数据的交换。例如,当某个线程接收到客户端发送的数据时,可以通过信号将数据传递给主线程进行处理,并通过槽将处理结果发送回给客户端。 在使用多线程进行TCP通信时,需要注意线程间的同步和数据的一致性。Qt提供了一些线程相关的类和方法,如QMutex、QReadWriteLock、QSemaphore等,用于实现线程的同步和互斥访问。另外,Qt还提供了一些线程安全的容器类,如QQueue、QList等,用于在多线程环境下进行数据的共享和访问。 需要注意的是,使用多线程进行TCP通信可能会增加系统的复杂性和开销。在设计和实现时,需要综合考虑到性能、并发性和易用性等方面的因素。同时,还需要注意处理异常和错误情况,以提高系统的稳定性和可靠性。 综上所述,Qt提供了丰富的功能和工具来实现TCP多线程通信。通过合理的设计和实现,可以高效地进行线程间的通信,并实现稳定和可靠的TCP通信功能。 ### 回答3: Qt是一个跨平台的应用程序框架,提供了丰富的库和工具,用于开发图形界面程序。在Qt中,可以使用TCP协议进行多线程通信。 首先,我们需要使用QTcpServer类创建一个TCP服务器服务器监听指定的端口,等待客户端的连接。当有客户端连接时,服务器会触发一个信号,我们可以在相应的槽函数中处理连接请求。 然后,我们可以使用QThread类创建多线程来处理客户端的请求。在每个线程中,我们可以使用QTcpSocket类与客户端进行通信。可以通过QTcpSocket的信号和槽函数来处理收到的数据,比如读取和发送数据。 在进行多线程通信时,需要注意线程之间的同步问题。Qt提供了一些线程间通信的方法,比如使用QMetaObject::invokeMethod()在不同线程之间调用函数,或者使用Qt的信号和槽机制来完成线程之间的数据传递。 此外,可以根据实际需求,进一步优化多线程通信。比如可以使用线程池来管理多个线程,避免频繁创建和销毁线程的开销;可以将数据的处理和传输部分分离,使得数据的处理和传输可以并行进行,提高效率。 总之,Qt提供了强大的功能来支持TCP多线程通信。通过合理地运用Qt提供的类和方法,我们可以轻松实现多线程TCP服务器和客户端,并进行高效可靠的通信
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值