当前位置: 首页 > news >正文

QT:TCP示例

QT 中 TCP 服务器端和客户端

在 QT 中,TCP(传输控制协议)是一种可靠的网络通信协议,常用于在客户端和服务器之间进行数据传输。服务器端负责监听特定端口,等待客户端连接;客户端则主动发起连接请求,连接到服务器端指定的 IP 地址和端口。

服务器

recvimg.h

#ifndef RECV_H
#define RECV_H

#include <QTcpServer>
#include <QTcpSocket>
#include <QObject>
#include <QByteArray>

constexpr int TCP_BUFFER_SIZE = 32 * 1024 * 1024;



class RecvServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit RecvServer(QObject *parent = nullptr);//explicit 禁止隐式类型转换
    ~RecvServer();

    bool startServer(quint16 port);

signals:
    void get_img(unsigned char*, struct U2DRealImgHead*);

private slots:
    void onNewConnection();
    void onReadyRead();
    void onDisconnected();

private:
    QByteArray buffer;
};
#endif // RECV_H

recvimg.cpp

#include "recvimg.h"
#include <QDebug>

RecvServer::RecvServer(QObject *parent) : QTcpServer(parent)
{
    qDebug() << "RecvServer";
}

RecvServer::~RecvServer()
{
    if (isListening()) {
        close();
    }
}

bool RecvServer::startServer(quint16 port)
{
    bool result = listen(QHostAddress::Any, port);
    if (result) {
        qDebug() << "Server started and listening on port" << port;
    } else {
        qDebug() << "Failed to start server on port" << port;
    }

    connect(this, &RecvServer::newConnection, this, &RecvServer::onNewConnection);
    return result;
}

void RecvServer::onNewConnection()
{
    QTcpSocket *socket = nextPendingConnection();
    connect(socket, &QTcpSocket::readyRead, this, &RecvServer::onReadyRead);
    connect(socket, &QTcpSocket::disconnected, this, &RecvServer::onDisconnected);

    qDebug() << "New client connected";
}

void RecvServer::onReadyRead()
{
    QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
    if (!socket) return;

    QByteArray data = socket->readAll();
    buffer.append(data);

    qDebug() << "Received" << data.size() << "bytes of data";


}

void RecvServer::onDisconnected()
{
    QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
    if (!socket) return;

    socket->deleteLater();
    qDebug() << "Client disconnected";
}



main.cpp

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

    xQLabel = new QLabel(this);
    //给服务器指针实例化对象
    server = new RecvServer(this);  //服务器创建完成
    quint16 port = 27015;

    if (server->startServer(27015)) {
        qDebug() << "Server started on port 27015";
    } else {
        qDebug() << "Failed to start server";
    }
   
    connect(server, &RecvServer::get_img, this, &MainWindow::imgCallback);
}

client

client.h

#ifndef CLIENT_H
#define CLIENT_H

#include <QObject>
#include <QTcpSocket>

class Client : public QObject
{
    Q_OBJECT
public:
    explicit Client(QObject *parent = nullptr);
    ~Client();

    bool connectToServer(const QString &host, quint16 port);
    void sendData(const QByteArray &data);

private slots:
    void onConnected();
    void onDisconnected();
    void onReadyRead();
    void onError(QAbstractSocket::SocketError socketError);

private:
    QTcpSocket *m_socket;
};

#endif // CLIENT_H

client.cpp

#include "fileclient.h"
#include <QDebug>

Client::Client(QObject *parent) : QObject(parent)
{
    m_socket = new QTcpSocket(this);

    connect(m_socket, &QTcpSocket::connected, this, &Client::onConnected);
    connect(m_socket, &QTcpSocket::disconnected, this, &Client::onDisconnected);
    connect(m_socket, &QTcpSocket::readyRead, this, &Client::onReadyRead);
    // 使用 errorOccurred 信号替代已弃用的 error 信号
    connect(m_socket, &QTcpSocket::errorOccurred, this, &Client::onError);
}

Client::~Client()
{
    if (m_socket->state() == QAbstractSocket::ConnectedState) {
        m_socket->disconnectFromHost();
        m_socket->waitForDisconnected();
    }
}

bool Client::connectToServer(const QString &host, quint16 port)
{
    m_socket->connectToHost(host, port);
    return m_socket->waitForConnected();
}

void Client::sendData(const QByteArray &data)
{
    if (m_socket->state() == QAbstractSocket::ConnectedState) {
        m_socket->write(data);
        m_socket->waitForBytesWritten();
    }
}

void Client::onConnected()
{
    qDebug() << "Connected to server";
}

void Client::onDisconnected()
{
    qDebug() << "Disconnected from server";
}

void Client::onReadyRead()
{
    QByteArray data = m_socket->readAll();
    qDebug() << "Received from server:" << data;
}

void Client::onError(QAbstractSocket::SocketError socketError)
{
    qDebug() << "Socket error:" << m_socket->errorString();
}

main.cpp

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

    Client client;
    if (client.connectToServer("192.168.20.48", 27015)) {
        // 发送测试数据,这里添加分隔符 "23456"
        QByteArray testData = "This is a test data segmen23456Another test data 23456";
        client.sendData(testData);
    }

}

定义的区别

RecvServer* server; server = new RecvServer(this); 
RecvServer server; 定义的区别
  1. 内存分配

RecvServer* server; server = new RecvServer(this);:
首先声明了一个RecvServer类型的指针server。
接着使用new操作符在堆(heap)上分配内存来创建一个RecvServer对象,并将对象的地址赋值给server指针。堆内存的分配较为灵活,适用于需要在程序运行期间动态创建和销毁对象,且对象生命周期可能跨越多个函数调用或作用域的场景。例如,在一个需要长期运行的服务器程序中,服务器对象可能需要在整个程序生命周期内持续存在,此时在堆上分配内存更为合适。
RecvServer server;:
直接在栈(stack)上创建一个RecvServer对象。栈内存的分配和释放由编译器自动管理,速度相对较快。栈内存的大小通常是有限的,且对象的生命周期与声明它的作用域紧密相关。例如,在一个简单的函数内部,如果只是临时需要一个RecvServer对象来完成一些短期任务,在栈上创建即可,当函数执行结束,对象会自动被销毁,释放占用的栈空间。
2. 生命周期管理
RecvServer* server; server = new RecvServer(this);:
由于对象在堆上分配,其生命周期不由声明它的作用域决定。需要程序员手动调用delete操作符来释放内存,否则会导致内存泄漏。例如,如果在一个类的成员函数中创建了这样的对象,在类的析构函数中需要检查该指针并调用delete,以确保对象占用的内存被正确释放。
RecvServer server;:
当对象所在的作用域结束时,编译器会自动调用对象的析构函数,释放对象占用的栈内存。例如,在一个函数内部声明的RecvServer对象,当函数执行完毕,对象会被自动销毁,无需手动干预。
3. 作用域
RecvServer* server; server = new RecvServer(this);:
只要指针server在作用域内有效,指向的对象就可以被访问,即使创建对象的作用域已经结束。例如,在一个函数中创建了堆上的RecvServer对象并返回指针,在调用该函数的其他函数中仍然可以通过该指针访问对象。
RecvServer server;:
对象的作用域仅限于声明它的代码块。例如,在一个函数内部的if语句块中声明的RecvServer对象,当if语句块结束,对象就超出了作用域,无法再被访问。

QByteArray buffer 的接口使用

QByteArray 是 Qt 框架中用于处理字节数组的类,它提供了丰富的接口,方便进行数据的存储、操作和转换。

构造函数

QByteArray 有多种构造函数,可用于创建不同类型的字节数组。

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    // 构造一个空的 QByteArray
    QByteArray emptyArray;

    // 从 const char* 构造 QByteArray
    const char* str = "Hello, World!";
    QByteArray arrayFromCStr(str);

    // 构造指定大小并填充特定字符的 QByteArray
    QByteArray filledArray(10, 'A');

    qDebug() << "Empty Array:" << emptyArray;
    qDebug() << "Array from C string:" << arrayFromCStr;
    qDebug() << "Filled Array:" << filledArray;

    return a.exec();
}

追加数据

可以使用 append() 方法向 QByteArray 中追加数据

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    QByteArray array("Hello");
    array.append(", World!");

    qDebug() << "Appended Array:" << array;

    return a.exec();
}

插入数据

使用 insert() 方法在指定位置插入数据。

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    QByteArray array("Hello World!");
    array.insert(5, " Qt");

    qDebug() << "Inserted Array:" << array;

    return a.exec();
}

删除数据

使用 remove() 方法删除指定位置和长度的数据。

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    QByteArray array("Hello Qt World!");
    array.remove(5, 3);

    qDebug() << "Removed Array:" << array;

    return a.exec();
}

访问单个字节

可以使用 [] 运算符或 at() 方法访问 QByteArray 中的单个字节。

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    QByteArray array("Hello");

    // 使用 [] 运算符访问
    char firstChar1 = array[0];

    // 使用 at() 方法访问
    char firstChar2 = array.at(0);

    qDebug() << "First character using []:" << firstChar1;
    qDebug() << "First character using at():" << firstChar2;

    return a.exec();
}

转换为 const char*

使用 constData() 方法将 QByteArray 转换为 const char*。

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    QByteArray array("Hello, World!");
    const char* cStr = array.constData();

    qDebug() << "Converted C string:" << cStr;

    return a.exec();
}

转换为整数

使用 toInt() 方法将 QByteArray 转换为整数。

#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>

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

    QByteArray array("123");
    bool ok;
    int num = array.toInt(&ok);

    if (ok) {
        qDebug() << "Converted integer:" << num;
    } else {
        qDebug() << "Conversion failed.";
    }

    return a.exec();
}

signals

在 Qt 中,signals 是一个特殊的关键字,用于标识接下来的内容是信号的声明。信号是 Qt 实现事件驱动编程的一种机制,当特定的事件发生时,对象可以发出信号,其他对象可以连接到这些信号并执行相应的槽函数(slot)。信号只能在继承自 QObject 类的子类中声明,并且需要在类定义中使用 Q_OBJECT 宏。

相关文章:

  • linux查看定时任务与设置定时任务
  • C#的判断语句总结
  • C++面试题:C++怎么避免头文件循环引用?
  • 游戏引擎学习第146天
  • 学习小程序开发--Day1
  • 学习网络安全需要哪些基础?
  • C++单例进化论
  • P8686 [蓝桥杯 2019 省 A] 修改数组--并查集 or Set--lower_bound()的解法!!!
  • 设计模式 一、软件设计原则
  • Spring源码探析(二):BootstrapContext初始化深度解析(默认配置文件加密实现原理)
  • [算法笔记]cin和getline的并用、如何区分两个数据对、C++中std::tuple类
  • uniapp版本加密货币行情应用
  • Unity DOTS从入门到精通之EntityCommandBufferSystem
  • C#模拟鼠标点击,模拟鼠标双击,模拟鼠标恒定速度移动,可以看到轨迹
  • 【vitepress】如何搭建并部署自己的博客网站
  • sqlserver中的锁模式 | SQL SERVER如何开启MVCC(使用row-versioning)【启用行版本控制减少锁争用】
  • 基于单片机的智慧农业大棚系统(论文+源码)
  • mysql(community版)压缩包安装教程
  • 【计算机网络】确认家庭网络是千兆/百兆带宽并排查问题
  • 解决电脑问题(5)——鼠标问题
  • 重庆大学:对学术不端行为“零容忍”,发现一例、查处一例
  • 时代中国控股:前4个月销售额18.1亿元,境外债重组协议押后聆讯至5月底
  • 巴基斯坦军方:印度导弹袭击巴首都附近空军基地
  • 巴基斯坦称成功拦截印度导弹,空军所有资产安全
  • 马上评丨行人转身相撞案:走路该保持“安全距离”吗
  • 一周文化讲座|城市移民与数字时代的新工作