用QT做一个网络调试助手

文章目录

  • 前言
  • 一、TCP网络调试助手介绍
    • [1. 项目概述](#1. 项目概述)
    • [2. 开发流程](#2. 开发流程)
    • [3. TCP服务器的关键流程](#3. TCP服务器的关键流程)
    • [4. TCP客户端的关键流程](#4. TCP客户端的关键流程)
  • 二、实现UI界面
    • [1. 服务器界面](#1. 服务器界面)
    • [2. 客户端界面](#2. 客户端界面)
  • 三、实现代码框架
    • [1. 服务器代码](#1. 服务器代码)
      • [1.1 初始化服务器地址](#1.1 初始化服务器地址)
      • [1.2 开始监听](#1.2 开始监听)
      • [1.3 与客户端连接](#1.3 与客户端连接)
      • [1.4 接收客户端信息](#1.4 接收客户端信息)
      • [1.5 判断客户端状态(连接或断开)](#1.5 判断客户端状态(连接或断开))
      • [1.6 停止监听或断开退出](#1.6 停止监听或断开退出)
      • [1.7 显示连接多个客户端](#1.7 显示连接多个客户端)
      • [1.8 给多个客户端发消息](#1.8 给多个客户端发消息)
    • [2. 客户端代码](#2. 客户端代码)
      • [2.1 连接服务器](#2.1 连接服务器)
      • [2.2 断开服务器](#2.2 断开服务器)
      • [2.3 自定义文本颜色](#2.3 自定义文本颜色)
      • [2.4 实现收发数据](#2.4 实现收发数据)

前言

完整代码:网络调试助手

一、TCP网络调试助手介绍

1. 项目概述

用 QT 模拟网络调试助手的服务器和客户端,可以进行数据收发。

服务器:

客户端:

2. 开发流程

3. TCP服务器的关键流程

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

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

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

4. TCP客户端的关键流程

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

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

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

二、实现UI界面

1. 服务器界面

首先我们先放入两个文本编辑框 Text Edit,一个用来发送数据,一个用来接收数据,并放入一个发送按钮与下面的文本编辑框水平对齐:

接着我们再放入三个按钮分别为监听,停止监听,断开,设置水平布局:

然后再放入标签 Label,组合框 Combo Box,行编辑框 Line Edit,设置水平布局:

最后再放入一个组合框 Combo Box,整体垂直布局,修改间距,设置标题:

2. 客户端界面

首先我们先放入两个文本编辑框 Text Edit,一个用来发送数据,一个用来接收数据,并放入一个发送按钮与下面的文本编辑框水平对齐:

接着我们再放入两个按钮分别为连接和断开,并放入两个标签 Label 跟两个行编辑框 Line Edit设置水平布局:

最后再总体进行垂直布局,调整间距大小,设置标题:

三、实现代码框架

1. 服务器代码

实现服务器框架步骤:

  1. 初始化服务端地址
  2. 开始监听
  3. 与客户端连接
  4. 接收客户端信息
  5. 判断客户端状态(连接或断开)
  6. 停止监听或断开退出
  7. 新建MyComboBox,使用组合框,显示多个客户端连接(记得提升)
  8. 实现发送消息给客户端,通过索引发送多个

1.1 初始化服务器地址

查看QT提供的帮助手册:

代码示例:

c++ 复制代码
#include <QTcpServer>
#include <QNetworkInterface>

public:
    QTcpServer* server;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    server = new QTcpServer(this);
    
    // 初始化服务器地址
    QList<QHostAddress> address = QNetworkInterface::allAddresses();
    for (QHostAddress tmp : address)
    {
        if (tmp.protocol() == QAbstractSocket::IPv4Protocol)
        {
            ui->comboBoxIP->addItem(tmp.toString());
        }
    }
}

程序运行结果:

1.2 开始监听

实现步骤:

  1. 设置端口号
  2. 开始监听

代码示例:

c++ 复制代码
#include <QMessageBox>

void Widget::on_btnListen_clicked()
{
    // 1. 设置端口号
    int port = ui->lineEditPort->text().toInt();

    // 2. 开始监听
    if (!server->listen(QHostAddress(ui->comboBoxIP->currentText()), port))
    {
        QMessageBox msgBox;
        msgBox.setWindowTitle("监听失败");
        msgBox.setText("端口号被占用");
        msgBox.exec();
        return;
    }

    ui->btnListen->setEnabled(false);
    ui->btnStopListen->setEnabled(true);
    ui->btnDisconnect->setEnabled(true);
}

程序运行结果:

1.3 与客户端连接

代码示例:

c++ 复制代码
private slots:
    void on_newClient_connect();

#include <QTcpSocket>

void Widget::on_newClient_connect()
{
    if (server->hasPendingConnections())
    {
        // 从TcpSocket中获得客户端地址和端口号
        QTcpSocket *connection = server->nextPendingConnection();
        ui->textEditRecv->insertPlainText("客户端地址:" + connection->peerAddress().toString()
                                          + "客户端端口号:" + QString::number(connection->peerPort()) + "\n");
    }
}

程序运行结果:

1.4 接收客户端信息

代码示例:

c++ 复制代码
private slots:
    void on_readyRead_handler();
    
void Widget::on_newClient_connect()
{
    if (server->hasPendingConnections())
    {
        // 从TcpSocket中获得客户端地址和端口号
        QTcpSocket *connection = server->nextPendingConnection();
        ui->textEditRecv->insertPlainText("客户端地址:" + connection->peerAddress().toString()
                                          + "客户端端口号:" + QString::number(connection->peerPort()) + "\n");

        // 接收客户端信息
        connect(connection, SIGNAL(readyRead()),this, SLOT(on_readyRead_handler()));

    }
}

void Widget::on_readyRead_handler()
{
    QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());

    QByteArray revData = tmpSock->readAll();
    ui->textEditRecv->insertPlainText("客户端: " + revData);
}

程序运行结果:

1.5 判断客户端状态(连接或断开)

代码示例:

c++ 复制代码
private slots:
    void mstateChange(QAbstractSocket::SocketState);
    
void Widget::on_newClient_connect()
{
    if (server->hasPendingConnections())
    {
        // 从TcpSocket中获得客户端地址和端口号
        QTcpSocket *connection = server->nextPendingConnection();
        ui->textEditRecv->insertPlainText("客户端地址:" + connection->peerAddress().toString()
                                          + "客户端端口号:" + QString::number(connection->peerPort()) + "\n");

        // 接收客户端信息
        connect(connection, SIGNAL(readyRead()),this, SLOT(on_readyRead_handler()));

        // 判断客户端状态
        connect(connection, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(mstateChange(QAbstractSocket::SocketState)));
    }
}

void Widget::mstateChange(QAbstractSocket::SocketState socketState)
{
    QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());
        qDebug() << "client out In state:" << socketState;
    switch (socketState) {
    case QAbstractSocket::UnconnectedState:
            ui->textEditRecv->insertPlainText("客户端断开");
            tmpSock->deleteLater();
        break;
    case QAbstractSocket::ConnectedState:
    case QAbstractSocket::ConnectingState:
            ui->textEditRecv->insertPlainText("客户端接入");
        break;
    }
}

程序运行结果:

1.6 停止监听或断开退出

代码示例:

c++ 复制代码
void Widget::on_btnStopListen_clicked()
{
    // 停止监听所有客户端
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
    for (QTcpSocket* tmp : tcpSocketClients)
    {
        tmp->close();
    }

    server->close();
    ui->btnListen->setEnabled(true);
    ui->btnStopListen->setEnabled(false);
    ui->btnDisconnect->setEnabled(false);
}

void Widget::on_btnDisconnect_clicked()
{
    on_btnStopListen_clicked();
    delete server;
    this->close();
}

程序运行结果:

1.7 显示连接多个客户端

新建 MyComboBox 类:

新建MyComboBox,使用组合框,显示多个客户端连接(记得提升)

代码示例:

myCombobox.h

c++ 复制代码
#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

myCombobox.cpp

c++ 复制代码
#include "mycombobox.h"

#include <QMouseEvent>

MyComboBox::MyComboBox(QWidget *parent) : QComboBox(parent)
{

}

void MyComboBox::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton){
        emit on_ComboBox_clicked();
    }
    QComboBox::mousePressEvent(e);
}

widget.h

c++ 复制代码
private slots:
    void mComboBox_refresh();

widget.cpp

c++ 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    server = new QTcpServer(this);

    // 初始化服务器地址
    QList<QHostAddress> address = QNetworkInterface::allAddresses();
    for (QHostAddress tmp : address)
    {
        if (tmp.protocol() == QAbstractSocket::IPv4Protocol)
        {
            ui->comboBoxIP->addItem(tmp.toString());
        }
    }

    // 与客户端连接
    connect(server, SIGNAL(newConnection()), this, SLOT(on_newClient_connect()));
    ui->btnStopListen->setEnabled(false);
    ui->btnDisconnect->setEnabled(false);
    ui->btnSend->setEnabled(false);

    // 刷新 combox
    connect(ui->comboBoxChildren,&MyComboBox::on_ComboBox_clicked,this,&Widget::mComboBox_refresh);
}

void Widget::mComboBox_refresh()
{
    ui->comboBoxChildren->clear();
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();

    for(QTcpSocket* tmp:tcpSocketClients){
        if(tmp!=nullptr)
            ui->comboBoxChildren->addItem(QString::number(tmp->peerPort()));
    }
    ui->comboBoxChildren->addItem("all");
}

程序运行结果:

1.8 给多个客户端发消息

代码示例:

c++ 复制代码
private:
    int childIndex;
    
void Widget::on_btnSend_clicked()
{
    QList<QTcpSocket*> tcpSocketClients =  server->findChildren<QTcpSocket*>();
    // 当用户不选择向all,所有客户端进行发送的时候
    qDebug() << childIndex;
    if(ui->comboBoxChildren->currentText() != "all")
    {
        // 根据用户选择,找到指定客户端进行数据通信,通过childInde来查找,该变量在用户选择条目后触发的on_comboBoxChildren_activated去赋值
        tcpSocketClients[childIndex]->write(ui->textEditSend->toPlainText().toStdString().c_str());
    }
    else
    {
        // 遍历所有子客户端,并一一调用write函数,向所有客户端发送
        for(QTcpSocket* tmp:tcpSocketClients)
        {
           QByteArray sendData = ui->textEditSend->toPlainText().toLocal8Bit();
           tmp->write(sendData);
        }
    }
}

void Widget::on_comboBoxChildren_activated(int index)
{
    childIndex = index;
}

程序运行结果:

2. 客户端代码

实现客户端框架步骤:

  1. 设置定时器判断连接是否成功(成功,超时,错误)
  2. 设置断开
  3. 自定义文本颜色
  4. 实现发送和接收数据

2.1 连接服务器

  1. 初始化client
  2. 设置定时器判断是否与服务器连接

代码示例:

c++ 复制代码
private slots:
    void on_btnConnect_clicked();

    void onConnected();

    void onError(QAbstractSocket::SocketError);

    void onTimeOUt();
    
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    ui->btnDisconnect->setEnabled(false);
    ui->btnSend->setEnabled(false);
    
    // 初始化client
    client = new QTcpSocket(this);
    connect(client,SIGNAL(readyRead()),this,SLOT(mRead_Data_From_Server()));
}

void Widget::on_btnConnect_clicked()
{
    client->connectToHost(ui->lineEdit_IP->text(),ui->lineEdit_Port->text().toInt());

    // 设置定时器判断是否与服务器连接
    timer = new QTimer(this);
    timer->setSingleShot(true);
    // timer->setInterval(5000);

    connect(timer, SIGNAL(timeout()),this,SLOT(onTimeOUt()));
    connect(client,SIGNAL(connected()),this,SLOT(onConnected()));
    connect(client,SIGNAL(error(QAbstractSocket::SocketError)),
                          this,SLOT(onError(QAbstractSocket::SocketError)));

    this->setEnabled(false);
    timer->start(1000);
}

void Widget::onTimeOUt()
{
    ui->textEdit_Recv->insertPlainText("连接超时");
    client->abort();
    this->setEnabled(true);
}

void Widget::onConnected()
{
    timer->stop();
    this->setEnabled(true);
    ui->textEdit_Recv->append("连接成功!");
    ui->btnConnect->setEnabled(false);
    ui->lineEdit_Port->setEnabled(false);
    ui->lineEdit_IP->setEnabled(false);
    ui->btnDisconnect->setEnabled(true);
    ui->btnSend->setEnabled(true);
}

void Widget::onError(QAbstractSocket::SocketError error)
{
    qDebug() << "连接错误:" << error;
    ui->textEdit_Recv->insertPlainText("连接出问题啦:"+client->errorString());
    this->setEnabled(true);
    on_btnConnect_clicked();
}

程序运行结果:

2.2 断开服务器

代码示例:

c++ 复制代码
void Widget::on_btnDisconnect_clicked()
{
    client->disconnectFromHost();
    client->close();
    ui->textEdit_Recv->append("断开连接");
    ui->btnConnect->setEnabled(true);
    ui->btnDisconnect->setEnabled(false);
    ui->lineEdit_Port->setEnabled(true);
    ui->lineEdit_IP->setEnabled(true);
    ui->btnSend->setEnabled(true);
}

程序运行结果:

2.3 自定义文本颜色

代码示例:

c++ 复制代码
private:
    void mInserTextByColor(Qt::GlobalColor color,QString str);
    
void Widget::mInserTextByColor(Qt::GlobalColor color,QString str)
{
    // 获取文本编辑器的光标位置,并将其存储在cursor变量中。
    QTextCursor cursor =  ui->textEdit_Recv->textCursor();
    
    // 创建一个QTextCharFormat对象,用于设置文本格式。
    QTextCharFormat format;
    
    // 使用setForeground方法设置文本的前景色为传入的颜色。
    format.setForeground(QBrush(QColor(color)));
    
    // 将格式化后的字符格式应用到光标上。
    cursor.setCharFormat(format);
    
    // 使用insertText方法在光标位置插入传入的字符串str。
    cursor.insertText(str);
}

2.4 实现收发数据

代码示例:

c++ 复制代码
// 发送为红色字体
void Widget::on_btnSend_clicked()
{
    QByteArray sendData = ui->textEdit_Send->toPlainText().toUtf8();
    client->write(sendData);

    // 客户端发送数据,并以红色字体插入到文本编辑器中
    mInserTextByColor(Qt::red, sendData);
}


// 接收为黑色字体
void Widget::mRead_Data_From_Server()
{
    // 将文本编辑器的光标移动到文本末尾
    ui->textEdit_Recv->moveCursor(QTextCursor::End);

    // 确保光标可见
    ui->textEdit_Recv->ensureCursorVisible();

    // 客户端读取所有数据,并以黑色字体插入到文本编辑器中
    mInserTextByColor(Qt::black, client->readAll());
}

程序运行结果:

相关推荐
liyuanbhu17 分钟前
Qt Creator 中使用 vcpkg
qt·cmake
忆源23 分钟前
SOME/IP--协议英文原文讲解2
网络·网络协议·tcp/ip
doubt。23 分钟前
3.[羊城杯2020]easyphp
网络·安全·web安全·网络安全·php·代码复审
米码收割机34 分钟前
【PHP】基于 PHP 的图片管理系统(源码+论文+数据库+图集)【独一无二】
开发语言·数据库·php
我是唐青枫36 分钟前
Linux ifstat 命令使用详解
linux·运维·网络
莫名有雪41 分钟前
攻防世界_php_rce(ThinkPHP框架)
php
A.sir啊41 分钟前
爬虫基础(三)Session和Cookie讲解
运维·服务器·前端·网络·网络爬虫
yyytucj41 分钟前
优化 PHP-FPM 参数配置:实现服务器性能提升
服务器·开发语言·php
落——枫1 小时前
存储器知识点2
网络
逆风局?1 小时前
计算机网络的组成,功能
网络·计算机网络