QT之简陋版网络调试助手

1. 开发流程

1.1 Qtcp服务器的关键步骤

工程建立,需要在.pro****加入网络权限

• 创建一个基于 QTcpServer 的服务端涉及以下关键步骤:

  1. 创建并初始化QTcpServer 实例
    • 实例化 QTcpServer 。
    • 调用 listen 方法在特定端口监听传入的连接。
  2. 处理新连接
    • 为 newConnection 信号连接一个槽函数。
    • 在槽函数中,使用 nextPendingConnection 获取 QTcpSocket 以与客户端通信。
  3. 读取和发送数据
    • 通过连接 QTcpSocket 的 readyRead 信号来读取来自客户端的数据。
    • 使用 write 方法发送数据回客户端。
  4. 关闭连接
    • 在适当的时候关闭 QTcpSocket 。

1.2 QTtcp客户端的关键开发步骤

• 工程建立,需要在.pro加入网络权限

• 创建一个基于 QTcpSocket 的Qt客户端涉及以下步骤:

  1. 创建QTcpSocket 实例
    • 实例化 QTcpSocket 。
  2. 连接到服务器
    • 使用 connectToHost 方法连接到服务器的IP地址和端口。
  3. 发送数据到服务器
    • 使用 write 方法发送数据。
  4. 接收来自服务器的数据
    • 为 readyRead 信号连接一个槽函数来接收数据。
  5. 关闭连接
    • 关闭 QTcpSocket 连接。

2. TCP协议

• TCP(传输控制协议)是一种广泛使用的网络通信协议,设计用于在网络中的计算机之间可靠地传输数据。它是互联网协议套件的核心部分,通常与IP(互联网协议)一起使用,合称为TCP/IP。以下是TCP协议的一些基本特点:

  1. **面向连接:**在数据传输之前,TCP 需要在发送方和接收方之间建立一个连接。这包括三次握手过
    程,确保两端都准备好进行数据传输。
  2. **可靠传输:**TCP 提供可靠的数据传输服务,这意味着它保证数据包准确无误地到达目的地。如果发生数据丢失或错误,TCP 会重新发送数据包。
  3. **顺序控制:**TCP 保证数据包的传输顺序。即使数据包在网络中的传输顺序被打乱,接收方也能按照正确的顺序重组这些数据。
  4. **流量控制:**TCP 使用窗口机制来控制发送方的数据传输速率,以防止网络过载。这有助于防止接收方被发送方发送的数据所淹没。
  5. **拥塞控制:**TCP 还包括拥塞控制机制,用来检测并防止网络拥塞。当网络拥塞发生时,TCP 会减少其数据传输速率。
  6. **数据分段:**大块的数据在发送前会被分割成更小的段,以便于传输。这些段会被独立发送并在接收端重新组装。
  7. **确认和重传:**接收方对成功接收的数据包发送确认(ACK)信号。如果发送方没有收到确认,它会重传丢失的数据包。
  8. 终止连接:数据传输完成后,TCP 连接需要被正常关闭,这通常涉及到四次挥手过程。
    • TCP 适用于需要
    高可靠性的应用
    ,如网页浏览、文件传输、电子邮件等。然而,由于它的这些特性,TCP在处理速度上可能不如其他协议(如UDP)那么快速。
    • TCP协议中的三次握手和四次挥手是建立和终止连接的重要过程。下面是它们的简要描述:

1.2 三次握手(建立连接)

• 三次握手的主要目的是在两台设备之间建立一个可靠的连接。它包括以下步骤:

  1. SYN:客户端向服务器发送一个SYN(同步序列编号)报文来开始一个新的连接。此时,客户端进入SYN-SENT状态。
  2. SYN-ACK:服务器接收到SYN报文后,回复一个SYN-ACK(同步和确认)报文。此时服务器进入SYN-RECEIVED状态。
  3. ACK:客户端接收到SYN-ACK后,发送一个ACK(确认)报文作为回应,并进入ESTABLISHED(已建立)状态。服务器在收到这个ACK报文后,也进入ESTABLISHED状态。这标志着连接已经建立。
    • 如图:

1.3 四次挥手(断开连接)

• 四次挥手的目的是终止已经建立的连接。这个过程包括以下步骤:

  1. FIN:当通信的一方完成数据发送任务后,它会发送一个FIN(结束)报文来关闭连接。发送完FIN报文后,该方进入FIN-WAIT-1状态。
  2. ACK:另一方接收到FIN报文后,发送一个ACK报文作为回应,并进入CLOSE-WAIT状态。发送FIN报文的一方在收到ACK后,进入FIN-WAIT-2状态。
  3. FIN:在等待一段时间并完成所有数据的发送后,CLOSE-WAIT状态的一方也发送一个FIN报文来请求关闭连接。
  4. ACK:最初发送FIN报文的一方在收到这个FIN报文后,发送一个ACK报文作为最后的确认,并进入TIME-WAIT状态。经过一段时间后,确保对方接收到了最后的ACK报文,该方最终关闭连接。
    • 如图:

    • 在这两个过程中,三次握手主要确保双方都准备好进行通信,而四次挥手则确保双方都已经完成通信并同意关闭连接。

3. Socket

• Socket不是一个协议,而是一种编程接口(API)或机制,用于在网络中实现通信。Socket 通常在应用层和传输层之间提供一个端点,使得应用程序可以通过网络发送和接收数据。它支持多种协议,主要是 TCP 和 UDP。
以下是 Socket 的一些基本特点:
类型:有两种主要类型的 Sockets ------ TCP Socket(面向连接,可靠)和 UDP Socket(无连接, 不可靠)。
应用:在各种网络应用中广泛使用,如网页服务器、聊天应用、在线游戏等。
编程语言支持:大多数现代编程语言如 Python, Java, C++, 等都提供 Socket 编程的支持。
功能:提供了创建网络连接、监听传入的连接、发送和接收数据等功能。
QT: 在QT组件中,QTcpSocket用来管理和实现TCP Socket通信,QUdpSocket用来管理和实现 UDP Socket通信
• 总之,Socket 是实现网络通信的基础工具之一,它抽象化了网络层的复杂性,为开发者提供了一种相对简单的方式来建立和管理网络连接。

4. 创建QTtcp服务端

4.1 ui

4.2 代码

• widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QAbstractSocket>
#include <QDebug>
#include <QMessageBox>
#include <QNetworkInterface>
#include <QTcpServer>
#include <QTcpSocket>
#include "mycombobox.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //界面初始化
    this->setLayout(ui->verticalLayout);
    this->setFixedSize(796,746);
    //btnStopListen,btnLineout和btnSend这三个按键在未连接的时候是不能使用的
    ui->btnStopListen->setEnabled(false);
    ui->btnLineout->setEnabled(false);
    ui->btnSend->setEnabled(false);

    //创建TCP服务器
    server = new QTcpServer(this);//在当前窗口
    //创建信号与槽,当有客户端来连接时,就触发这个信号
    //当有新的客户端连接到服务器时,就发送一个newConnection信号
    connect(server,&QTcpServer::newConnection,this,&Widget::on_newClient_connect);

    //QList<QHostAddress> QNetworkInterface::allAddresses()
    //这个静态方法是用于获取主机上所有的IP地址
    QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
    //然后一个一个刷上comboBoxAddr这里面
    for(QHostAddress address : addresses){
        //然后一个个放到ui的comboBoxAddr这里,只放IPV4的
        //protocol() 是 "告诉我这个IP地址是IPv4还是IPv6"
        if(address.protocol() == QAbstractSocket::IPv4Protocol)
            ui->comboBoxAddr->addItem(address.toString());
    }

    //给on_ComboBox_clicked()创建信号与槽,刷新拉下列表
    connect(ui->comboBoxChildren,SIGNAL(on_ComboBox_clicked()),this,SLOT(mComboBox_refresh()));

}

void Widget::mComboBox_refresh(){
    //先清理一下comboBoxChildren
    ui->comboBoxChildren->clear();
    //获取所有的客户端连接
    //获取server对象下所有的 QTcpSocket 客户端连接。
    QList<QTcpSocket *> tcpSocketClients = server->findChildren<QTcpSocket *>();
    //把所有端口号添加到comboBoxChildren
    for(QTcpSocket* tmp : tcpSocketClients){
        //断开的客户端不应该加入进去,但是断开的客户端以0端口号还是会存在在server
        //如果不清理会影响重新连接的客户端
        if(tmp != nullptr)
            ui->comboBoxChildren->addItem(QString::number(tmp->peerPort()));
    }
    //在添加all
    ui->comboBoxChildren->addItem("all");//选择全部

}
void Widget::on_readyRead_Handler(){
    //sender()获得是那个socket通道发来的信号的。(可能连接了多个客户端)
    //找到那个通道连接的那个对象QTcpSocket
    QTcpSocket* tmpSocket = qobject_cast<QTcpSocket *>(sender());
    //获取数据
//    QString revDate = tmpSocket->readAll();
//    revDate = revDate.toUtf8();
    QString revDate = tmpSocket->readAll();
    QString data = revDate.toUtf8();
    //把数据放到textEditRev上面
    ui->textEditRev->insertPlainText("客户端" + QString::number(tmpSocket->peerPort()) + ":" + data + "\n");
    //让滚动条一直在下面,并且显示光标
    ui->textEditRev->moveCursor(QTextCursor::End);
    ui->textEditRev->ensureCursorVisible();
}


void Widget::msocketState(QAbstractSocket::SocketState socketState){
    int tmpIndex;
    qDebug() << "client out In state: " << socketState;
    //找出是那个客户端点击了断开连接
    QTcpSocket* tmpSocket = qobject_cast<QTcpSocket *>(sender());
    switch(socketState){
        case QAbstractSocket::UnconnectedState://这个表示客户端断开连接或者没有连接
            //QComboBox::findText() 是在 QComboBox 中查找指定文本并返回其索引的函数。
            tmpIndex =  ui->comboBoxChildren->findText(QString :: number(tmpSocket->peerPort()));
            //根据索引去掉comboBoxChildren里面的端口
            ui->comboBoxChildren->removeItem(tmpIndex);
            //然后清理该端口的资源
            //deleteLater() 是 "请晚一点再删除我",告诉Qt:"等当前事件处理完了,在下一轮事件循环中安全地删除这个对象"
            tmpSocket->deleteLater();
            if(ui->comboBoxChildren->count() == 0){
                //判断还有没有客户端在线
                //如果没有的话,此时是不能发消息的
                ui->btnSend->setEnabled(false);
            }
            ui->textEditRev->insertPlainText("客户端" + QString :: number(tmpSocket->peerPort())
                                             + ":" + "断开连接" + "\n");
            break;
    }

}
void Widget::on_newClient_connect(){
    qDebug() << "newClient In";
    //检查服务器的 待处理连接队列中是否有客户端在等待
    //多个客户端连接的时候
    if(server->hasPendingConnections()){
        //获得QTcpSocket以与客户端通信
        QTcpSocket* connection = server->nextPendingConnection();
        //打印客户端的地址和端口号
        qDebug() << "Client Addr: " << connection->peerAddress().toString()
                 << "Port: " << connection->peerPort();
        //将连接显示到textEditRev上面
        ui->textEditRev->insertPlainText("客户端地址:" + connection->peerAddress().toString()
                                         + "\n客户端端口号:" + QString :: number(connection->peerPort()) + "\n");

        //创建接收到客户端数据的信号与槽
        //当连接成功的时候,就进行数据交互,本质还是IO操作
        //readyRead() 信号是 "有新的数据可以读了" 的通知
        connect(connection,SIGNAL(readyRead()),this,SLOT(on_readyRead_Handler()));
        //创建客户端断开的信号与槽
        //方式一,void QAbstractSocket::stateChanged(QAbstractSocket::SocketState socketState)
        //当 QAbstractSocket 的状态发生变化时,会发出这个信号
        connect(connection,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
                this,SLOT(msocketState(QAbstractSocket::SocketState)));

        //处理连接的时候,把加进来的连接的端口放到comboBoxChildren
        //但是仅仅只在ui上面
        //setCurrentText() 是 "请在下拉框/组合框中显示这个文本"
        //把连接进来的客户端端口号放进comboBoxChildren
        ui->comboBoxChildren->addItem(QString::number(connection->peerPort()));
        ui->comboBoxChildren->setCurrentText(QString::number(connection->peerPort()));


        //有连接就恢复发送按键
        //isChecked() 是用于查询按钮类控件是否被选中的函数,返回 true 表示选中/勾选,false 表示未选中。
        if(!ui->btnSend->isChecked()){
            ui->btnSend->setEnabled(true);
        }
    }
}

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


void Widget::on_btnListen_clicked()
{
    //开始监听
    //bool QTcpServer::listen
    //(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
    //QHostAddress要检查的IP地址,port就是端口
    //重新构造,将字符串IP地址转化为QHostAddress这个类型
    //QHostAddress addr();
    //QHostAddress::Any 是自动检查并同时监听 IPv4 和 IPv6 接口。
    //listen() 返回 true 表示监听成功,false 表示监听失败。
    int port = ui->lineEditPort->text().toInt();//端口
    if(!server->listen(QHostAddress(ui->comboBoxAddr->currentText()),port)){//监听不成功的时候
        qDebug() << "listenError";
        QMessageBox msgBox;
        msgBox.setWindowTitle("监听失败");
        msgBox.setText("端口被占用!");
        msgBox.exec();
        return;
    }
    //监听成功
    ui->btnListen->setEnabled(false);
    ui->btnStopListen->setEnabled(true);
    ui->btnLineout->setEnabled(true);

}

void Widget::on_btnStopListen_clicked()
{
    //关闭所有的QTcpSocket
    // QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>("widgetname");
    //在当前对象的所有子对象中查找指定名称和类型的对象
    //获取server对象下所有的 QTcpSocket 客户端连接。
    QList<QTcpSocket *> tcpSocketClients = server->findChildren<QTcpSocket *>();//获得了所有的客户端
    //关闭所有客户端与服务端的连接
    for(QTcpSocket* tmp: tcpSocketClients){
        tmp->close();
    }
    //关闭服务端
    server->close();
    //重置按钮
    ui->btnListen->setEnabled(true);
    ui->btnStopListen->setEnabled(false);
    ui->btnLineout->setEnabled(false);
}

void Widget::on_btnLineout_clicked()
{
    //程序直接退出
    on_btnStopListen_clicked();
    //删除服务端
    delete server;
    //退出程序
    this->close();
}

void Widget::on_btnSend_clicked()
{
    // QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>("widgetname");
    //在当前对象的所有子对象中查找指定名称和类型的对象
    //获取server对象下所有的 QTcpSocket 客户端连接。

    //找出所有与客户端的连接
    QList<QTcpSocket *> tcpSocketClients = server->findChildren<QTcpSocket *>();
    //判断是否有连接
    if(tcpSocketClients.isEmpty()){
        QMessageBox msgBox;
        msgBox.setWindowTitle("发送失败");
        msgBox.setText("当前无连接!");
        msgBox.exec();
        return;
    }

    //判断是私发还是广播
    if(ui->comboBoxChildren->currentText() != "all"){//选择是那个端口号发送
        QString currentName = ui->comboBoxChildren->currentText();
        //查找是否有这个端口号
        for(QTcpSocket* tmp : tcpSocketClients){
            if(QString::number(tmp->peerPort()) == currentName){
                tmp->write(ui->textEditSend->toPlainText().toStdString().c_str());
            }
        }

    }else if(ui->comboBoxChildren->currentText() == "all"){
        //广播
        for(QTcpSocket* tmp : tcpSocketClients){
            tmp->write(ui->textEditSend->toPlainText().toStdString().c_str());
        }
    }
}

• widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QAbstractSocket>
#include <QTcpServer>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void on_newClient_connect();
    void on_readyRead_Handler();
    void msocketState(QAbstractSocket::SocketState);

     void mComboBox_refresh();

private slots:
    void on_btnListen_clicked();

    void on_btnStopListen_clicked();

    void on_btnLineout_clicked();

    void on_btnSend_clicked();

private:
    Ui::Widget *ui;

    QTcpServer *server;
};
#endif // WIDGET_H

• mycombobox.cpp

cpp 复制代码
#include "mycombobox.h"

#include <QMouseEvent>

//QT中没有点击ComboBox就刷新的信号
//所有需要重写鼠标点击事件,然后发送一个信号
mycombobox::mycombobox(QWidget *parent) : QComboBox(parent)
{

}

void mycombobox::mousePressEvent(QMouseEvent *e)
{
     //button() - 返回触发当前事件的特定按钮
    if(e->button() == Qt::LeftButton){
        emit on_ComboBox_clicked();
    }
    //继续传递下去,调用基类处理,确保正常弹出下拉列表
    // 缺少 QComboBox::mousePressEvent(e); 可能导致 ComboBox无法正常弹出选项列表
    QComboBox::mousePressEvent(e);
}

• mycombobox.h

cpp 复制代码
#ifndef MYCOMBOBOX_H
#define MYCOMBOBOX_H

#include <QComboBox>
#include <QWidget>

class mycombobox : public QComboBox
{
    Q_OBJECT
public:
    mycombobox(QWidget* parent);

protected:
    void mousePressEvent(QMouseEvent *e) override;
signals:
    void on_ComboBox_clicked();
};

#endif // MYCOMBOBOX_H

5. 创建QTtcp客户端

5.1 ui

5.2 代码

• widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QTcpSocket>
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //UI的初始化
    this->setLayout(ui->verticalLayout);
    //断开连接和发送在还有连接到服务端是不能操作的
    ui->btndisCon->setEnabled(false);
    ui->btnSend->setEnabled(false);

    //创建新的TcpSocket去连接服务端
    client = new QTcpSocket(this);
    //创建读取的信号与槽
    connect(client,SIGNAL(readyRead()),this,SLOT(mRead_Data_From_Server()));

}


void Widget::mRead_Data_From_Server(){
    //读取服务端发送过来的数据
    setInsertColor(Qt::black,client->readAll());
    //解决滚动条问题
    ui->textEditRev->moveCursor(QTextCursor::End);
    ui->textEditRev->ensureCursorVisible();


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


void Widget::on_btnConnect_clicked()
{
    //这个函数用来启动一个到远程服务器的连接,连接结果通过信号通知。
    //virtual void
    //connectToHost(const QString &hostName, quint16 port,
    //              QIODevice::OpenMode openMode = ReadWrite, QAbstractSocket::NetworkLayerProtocol protocol = AnyIPProtocol)

    //去连接服务端,根据IP和端口号
    client->connectToHost(ui->lineEditIPAddr->text(),ui->lineEditPort->text().toInt());

    //用于获取套接字的当前状态
    //这里有一个bug,就是错误的服务端IP地址都能连接成功,这是由于client->state() == QAbstractSocket::ConnectingState
    //这个条件造成的,因为正在连接也不知道能不能连接成功,只要是正在连接就过了这个条件
    //    if(client->state() == QAbstractSocket::ConnectingState ||
    //            client->state() == QAbstractSocket::ConnectedState){
    //        ui->textEditRev->append("连接成功!");
    //        ui->lineEditPort->setEnabled(false);
    //        ui->lineEditIPAddr->setEnabled(false);
    //        ui->btnConnect->setEnabled(false);

    //        ui->btndisCon->setEnabled(true);
    //        ui->btnSend->setEnabled(true);

    //    }

    //解决方法,超时判负
    timer = new QTimer(this);//创建定时器
    timer->setSingleShot(true);//设置为单次定时器
    timer->setInterval(5000);//间隔为5s

    //如果超时的操作
    connect(timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
    //所以解决方法还是用信号与槽,使用这个信号(判断客户端是否连接成功)
    //QAbstractSocket::connected() 是 Qt 网络编程中表示连接成功建立的信号。
    connect(client,SIGNAL(connected()),this,SLOT(onConnect()));

    //而且还需要做连接不成功的信号与槽,
    //QAbstractSocket::error() 是 Qt 网络编`程中表示发生错误的信号。
    connect(client,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError(QAbstractSocket::SocketError)));
    //不希望被按下一次连接之后,被多次按下,从而造成影响
    this->setEnabled(false);
    //开启定时器
    timer->start();
}

void Widget::onError(QAbstractSocket::SocketError socketError){
    qDebug() << "连接错误:" << socketError;//错误码打印出来
    //errorString() 是 "告诉我刚才发生了什么错误" 的错误信息查询函数,返回人类可读的错误描述。
    ui->textEditRev->append("连接出问题了:" + client->errorString());
    this->setEnabled(true);

    on_btndisCon_clicked();

}
void Widget::onConnect(){//连接成功就做这个事
    //关闭定时器
    timer->stop();
    ui->textEditRev->append("连接成功!");
    this->setEnabled(true);

    ui->lineEditPort->setEnabled(false);
    ui->lineEditIPAddr->setEnabled(false);
    ui->btnConnect->setEnabled(false);

    ui->btndisCon->setEnabled(true);
    ui->btnSend->setEnabled(true);



}
void Widget::onTimeout(){
    ui->textEditRev->append("连接超时!");
    //还要清理之前不正确的IP,不然会对正确的IP造成影响的
    client->abort();
    this->setEnabled(true);
}


void Widget::on_btndisCon_clicked()
{
    //disconnectFromHost() 是 "我要优雅地断开连接,把该发的数据都发完再走" 的礼貌断开函数。
    client->disconnectFromHost();
    client->close();

    ui->textEditRev->append(QString::number(client->peerPort()) + "断开连接!");
    ui->lineEditPort->setEnabled(true);
    ui->lineEditIPAddr->setEnabled(true);
    ui->btnConnect->setEnabled(true);

    ui->btndisCon->setEnabled(false);
    ui->btnSend->setEnabled(false);
}

void Widget::setInsertColor(Qt::GlobalColor color,QString str){
    //    //ui->textEditRev->setForegroundRole(),这个也可以设置颜色,
    //    //但是会统一设置的,没法做到颜色上区分,所以要使用光标级别的
    //    QTextCursor cursor = ui->textEditRev->textCursor();

    //    //void QTextCursor::setCharFormat(const QTextCharFormat &format)
    //    //用于设置文本字符格式。
    //    QTextCharFormat format;//QTextCharFormat 类是 Qt 中用于设置文本字符格式的类
    //    //用于设置前景画刷的函数。
    //    format.setForeground(QBrush(QColor(Qt::red)));
    //    cursor.setCharFormat(format);
    //    cursor.insertText(ui->textEditSend->toPlainText());

    //获得当前光标
    QTextCursor cursor = ui->textEditRev->textCursor();
    //QTextCursor::setCharFormat(const QTextCharFormat &format)是QTextCursor类的一个方法,用于应用字符格式到文本。
    QTextCharFormat format;
    //设置前景色
    format.setForeground(QBrush(QColor(color)));
    //应用字符格式到文本。
    cursor.setCharFormat(format);
    //insertText()是QTextCursor类的一个方法,用于在光标位置插入文本。
    cursor.insertText(str);
}

void Widget::on_btnSend_clicked()
{
    //获取要发送的数据
    QByteArray sendData = ui->textEditSend->toPlainText().toUtf8();
    //发送出去
    client->write(sendData);
    //区别客户端发送的消息和服务端发送过来的消息(用颜色区分)
    setInsertColor(Qt::red,sendData);//客户端发送,显示红色

}

• widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QTcpSocket>
#include <QTimer>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btnConnect_clicked();

    void onTimeout();

    void onConnect();

    void onError(QAbstractSocket::SocketError socketError);

    void on_btndisCon_clicked();

    void on_btnSend_clicked();
    void mRead_Data_From_Server();

private:
    Ui::Widget *ui;

    QTcpSocket* client;
    QTimer *timer;
    void setInsertColor(Qt::GlobalColor color,QString str);//用来设置颜色
};
#endif // WIDGET_H
相关推荐
小尧嵌入式5 小时前
C语言中的面向对象思想
c语言·开发语言·数据结构·c++·单片机·qt
程序员-King.5 小时前
【Qt开源项目】— ModbusScope-day 4
开发语言·qt
程序员-King.8 小时前
【Qt开源项目】— ModbusScope-day 5
开发语言·qt
老秦包你会8 小时前
QT第五课------QT系统相关------线程
开发语言·qt
淼淼76310 小时前
Qt拖动工具栏控件到图页中均匀展示
开发语言·c++·windows·qt
YouEmbedded12 小时前
解码信号与槽(含 QTimer 应用)
qt·定时器·信号与槽
小灰灰搞电子12 小时前
Qt SCXML 模块详解
开发语言·qt
开始了码12 小时前
UDP 协议详解与 Qt 实战应用
qt·网络协议·udp
深蓝海拓1 天前
PySide6从0开始学习的笔记(三) 布局管理器与尺寸策略
笔记·python·qt·学习·pyqt