当前位置:   article > 正文

Qt实践录:TCP网络调试助手_网络调试助手源码

网络调试助手源码

由于项目需要使用到网络调试及测试,为了练手,使用 Qt 编写一个串口调试助手。本文按开发的过程进行简单介绍,同时也涉及部分用到的模块代码。详细代码参考源码仓库。

在代码复用方面,笔者认为 Qt 比 MFC 好,比如主窗口代码可以直接使用,当然,还要修改工程文件名称和对应的依赖库,界面控件也要重新设计和实现。这也是笔者喜欢直接使用 Qt Creator 创建的默认文件、类的原因。另外,如果使用纯代码实现窗体布局,其复用程度比用 Qt Creator 更加好。 实际上,本文的工程,就是在前一文章 《Qt实践录:串口调试助手源码基础上修改而得的。窗体基本设置、图标、状态栏、十六进制显示,定时发送等等代码,直接沿用。因此,文中不再重复前文所涉及的模块。

工具特性

功能

  • TCP客户端、服务端。
  • 十六进制收、发。
  • 时间戳显示
  • 为方便测试,本工具同时具备服务端和客户端功能,可实现自发自收。也可单独使用。

已知 Bug

连接、断开等逻辑处理未完善。
针对服务器,理论上应该需要根据不同客户端发送数据(或定向,或全部),当前版本未实现,仅取最后一个客户端。

Qt 相关知识

  • MainWindow设计。
  • Qt TCP编程。
  • 常用控件:按钮、复选框、文本编辑框、控件贴图。应用程序logo。
  • button字体。
  • 文本编辑框自定义显示的文字颜色。

运行结果如图1所示:
在这里插入图片描述
图1

开发过程

工程相关

Qt 中网络相应的依赖库为network,需要在工程文件中添加对应的库,如下:

QT       += core gui network
  • 1

TCP编程

相关头文件及变量

#include <QTcpServer>
#include <QTcpSocket>

    QTcpServer *m_tcpServer;

    QList<QTcpSocket*> m_clientList;
    QTcpSocket *m_tcpCliSocket;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中,m_tcpServer 用于TCP服务器,m_tcpCliSocket 用于保存连接的客户端。m_tcpCliSocket 用于客户端连接。

服务端

创建服务端:
// server
m_tcpServer = new QTcpServer();

// 连接newConnection信号,svr_newConnect中处理客户端的连接
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(svr_newConnect()));

监听端口:
m_tcpServer->listen(QHostAddress::Any, port);

关闭:
m_tcpServer->close();

接收数据:
tcpSocket->readAll();

发送数据:
tcpSocket->write(sendData, sendData.size());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

当有新客户端连接时,会自动调用svr_newConnect函数,该函数保存客户端socket,并关联数据接收信号和槽:

void MainWindow::svr_newConnect()
{
    printDebugInfo("get new connect");
    QTcpSocket *tcpSocket = m_tcpServer->nextPendingConnection();//新的客户端发起的连接
    QHostAddress clientIp = tcpSocket->peerAddress();//客户端的IP
    quint16 port = tcpSocket->peerPort();//客户端的端口
    if(m_clientList.contains(tcpSocket))
    {
        printDebugInfo(QString("%1:%2 already connected").arg(clientIp.toString()).arg(port));
    }
    else
    {
        printDebugInfo(QString("new connect from %1:%2").arg(clientIp.toString()).arg(port));
        m_clientList.append(tcpSocket);//记录下客户端发起的连接
        connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(svr_disconnect()));
        connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); // 数据接收
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

当客户端发送数据时,会自动触发readyRead函数,该函数读取数据并显示:

void MainWindow::readyRead()
{
    QTcpSocket *tcpSocket = static_cast<QTcpSocket *>(QObject::sender());
    QByteArray buffer = tcpSocket->readAll();
    showRecvData("SERVER> ", buffer);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

客户端

服务端的IP和端口,由界面输入,连接服务端函数如下:

QHostAddress serverIp;
serverIp.setAddress(ui->cbRemoteIP->currentText());
uint16_t port=ui->cbRemotePort->currentText().toUShort();
m_tcpCliSocket->connectToHost(serverIp, port);
  • 1
  • 2
  • 3
  • 4

注意,不能使用isOpenisValid来判断是否连接成功,需要使用waitForConnected判断,示例如下:

if (!m_tcpCliSocket->waitForConnected(600))
{
    printDebugInfo("connect failed");
    return;
}
  • 1
  • 2
  • 3
  • 4
  • 5

客户端相关信号和槽:

// client
m_tcpCliSocket = new QTcpSocket();
connect(m_tcpCliSocket, SIGNAL(connected()), this, SLOT(cli_connected())); // 客户端连接
connect(m_tcpCliSocket, SIGNAL(disconnected()), this, SLOT(cli_disconnected())); // 客户端断开连接
connect(m_tcpCliSocket, SIGNAL(readyRead()), this, SLOT(cli_receiveData())); // 客户端接收数据
  • 1
  • 2
  • 3
  • 4
  • 5

当远程服务器发送数据到客户端时,会自动触发cli_receiveData函数:

void MainWindow::cli_receiveData()
{
    QByteArray buffer;
    buffer = m_tcpCliSocket->readAll();
    showRecvData("CLIENT> ", buffer);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

界面逻辑

界面设计

界面使用设计师进行设计,如图2所示。界面多数功能与前面文章一样,故不再涉及。
在这里插入图片描述
图2

文本颜色

本工程接收显示的文本有三种类型:时间戳+提示语、服务端数据、客户端数据,为了区别,使用不同颜色显示。核心代码如下:

    if (m_showTimestamp)
    {
        QDateTime dateTime(QDateTime::currentDateTime());
        timeStr = "[" + dateTime.toString("yyyy-MM-dd HH:mm:ss.zzz") + "] ";
    }

    if (m_recvHex == 1)
    {
        info = buffer.toHex(' ').data();
    }
    else
    {
        info = QString(buffer);
    }

    // 根据类型,使用不同颜色显示
    if (tips.contains("SERVER"))
    {
        info = "<font color=\"blue\">" + info + "</font>";
    }
    else
    {
        info = "<font color=\"green\">" + info + "</font>";
    }

    ui->txtRecv->appendHtml("<font color=\"gray\">" + timeStr + tips + "</font>");
    ui->txtRecv->appendHtml(info);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

利用 QPlainTextEdit 的 appendHtml 函数,可以使用 html 格式指定颜色。本工程中,提示语用灰色,服务端接收的数据用蓝色,客户端接收数据用绿色。

其它

笔者在此工具基础上实现了自定义二进制协议,并对 ESP8266 进行操作,包括指示LED灯、继电器、出厂恢复、FOTA固件升级以及运行态的功能测试验证等操作,同时整合了前面的串口功能,实现一个工具进行全功能测试。由于与本文关联不大,不再展开。仅以截图展示:
在这里插入图片描述

代码仓库

本工程所有源码均可自由自主使用,包括但不限于添加、删除、修改,商用、自用。由此带来的成果/后果概与作者无关。限于水平能力,本程序无任何质量保证,本程序作者无提供服务之义务。

仓库地址在此

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/583663
推荐阅读
相关标签
  

闽ICP备14008679号