IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Qt 的网络通信(TCP) -> 正文阅读

[系统运维]Qt 的网络通信(TCP)

基于TCP Qt的网络通信

在标准 C++ 没有提供专门用于套接字通信的类,所以只能使用操作系统提供的基于 C 的 API 函数,基于这些 C 的 API 函数我们也可以封装自己的 C++ 类 , 但是Qt 提供了封装好的套接字通信类:

  • QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接
  • QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接

这两个套接字通信类都属于网络模块 network ,使用前切记在项目的 .pro文件中添加 network 模块

1、QTcpSever

QTcpServer 类用于监听客户端连接以及和客户端建立连接,在使用之前先介绍一下这个类提供的一些常用 API 函数:

1.1 成员函数

  • 构造函数
QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);
  • 给监听的套接字设置监听
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
// 判断当前对象是否在监听, 是返回true,没有监听返回false
bool QTcpServer::isListening() const;
// 如果当前对象正在监听返回监听的服务器地址信息, 否则返回 QHostAddress::Null
QHostAddress QTcpServer::serverAddress() const;
// 如果服务器正在侦听连接,则返回服务器的端口; 否则返回0
quint16 QTcpServer::serverPort() const

参数:

  1. address: 通过类 QHostAddress 可以封装 IPv4 、 IPv6 格式的 IP地址,QHostAddress :: Any 表示自动绑定;
  2. port: 端口号 , 指定为 0 表示随机绑定一个可用端口;
  3. 返回值: 绑定失败返回 false , 成功 true;
  • 得到和客户端建立连接之后用于通信的 QTcpSocket 套接字对象,它是 QTcpServer 的一个子对象,当 QTcpServer 对象析构的时候会自动析构这个子对象,当然也可自己手动析构,建议用完之后自己手动析构这个通信的 QTcpSocket 对象。
QTcpSocket *QTcpServer::nextPendingConnection();
  • 阻塞等待客户端发起的连接请求,不推荐在单线程程序中使用,建议使用非阻塞方式处理新连接,即使用信号 newConnection()
bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = Q_NULLPTR);

参数:

  1. mes: 指定阻塞的最大时间 , 单位毫秒;
  2. timeout: 传出参数,如果操作超时 timeout 为 true,没有超时 timeout 为 false;

1.2 信号

  • 当接受新连接导致错误时,将发射如下信号。socketError 参数描述了发生的错误相关的信息;
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);
  • 每次有新连接可用时都会发出 newConnection () 信号。
[signal] void QTcpServer::newConnection();

2、 QTcpSocket

QTcpSocket 是一个套接字通信类,不管是客户端还是服务器端都需要使用。在 Qt 中发送和接收数据也属于 IO 操作(网络 IO),先来看一下这个类的继承关系:

QTcpSocket 继承于  QAbstractSocket  继承于  QIODevice

2.1 成员函数

  • 构造函数
QTcpSocket::QTcpSocket(QObject *parent = Q_NULLPTR);
  • 连接服务器,需要指定服务器端绑定的IP和端口信息。
[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);

[virtual] void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);

在 Qt 中不管调用读操作函数接收数据,还是调用写函数发送数据,操作的对象都是本地的由 Qt 框架维护的一块内存。因此,调用了发送函数数据不一定会马上被发送到网络中,调用了接收函数也不是直接从网络中接收数据,关于底层的相关操作是不需要使用者来维护的

  • 接受数据
// 指定可接收的最大字节数 maxSize 的数据到指针 data 指向的内存中
qint64 QIODevice::read(char *data, qint64 maxSize);
// 指定可接收的最大字节数 maxSize,返回接收的字符串
QByteArray QIODevice::read(qint64 maxSize);
// 将当前可用操作数据全部读出,通过返回值返回读出的字符串
QByteArray QIODevice::readAll();
  • 发送数据
// 发送指针 data 指向的内存中的 maxSize 个字节的数据
qint64 QIODevice::write(const char *data, qint64 maxSize);
// 发送指针 data 指向的内存中的数据,字符串以 \0 作为结束标记
qint64 QIODevice::write(const char *data);
// 发送参数指定的字符串
qint64 QIODevice::write(const QByteArray &byteArray);

2.2 信号

在使用 QTcpSocket 进行套接字通信的过程中,如果该类对象发射出 readyRead() 信号,说明对端发送的数据达到了,之后就可以调用 read 函数接收数据了

[signal] void QIODevice::readyRead();
  • 调用 connectToHost() 函数并成功建立连接之后发出 connected() 信号
[signal] void QAbstractSocket::connected();
  • 在套接字断开连接时发出 disconnected() 信号
[signal] void QAbstractSocket::disconnected();

3、 通信流程

代码百度连接,自行提取

链接:https://pan.baidu.com/s/1W-ixxxt66IsmDLXUTD0PsA
提取码:1234

3.1 服务器端

  1. 创建套接字服务器 QTcpServer 对象
  2. 通过 QTcpServer 对象设置监听,即:QTcpServer::listen()
  3. 基于 QTcpServer::newConnection() 信号检测是否有新的客户端连接
  4. 如果有新的客户端连接调用 QTcpSocket *QTcpServer::nextPendingConnection() 得到通信的套接字对象
  5. 使用通信的套接字对象 QTcpSocket 和客户端进行通信

在这里插入图片描述

头文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QString>
#include <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_setlisten_clicked();

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QTcpServer* m_s;
    QTcpSocket* m_tcp;
    QLabel* m_status;
};

#endif // MAINWINDOW_H

源文件:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("服务器");
    ui->pushButton->setDisabled(true); // 初始化连接前,不能按发送按钮

    m_s = new QTcpServer(this);// 创建监听的套接字

    ui->port->setText("8899"); // 默认资源
    connect(m_s ,&QTcpServer::newConnection ,this,[=](){
        // 创建用于通信的套接字
        m_tcp = m_s->nextPendingConnection();

        m_status->setPixmap(QPixmap(":/green.png").scaled(20,20));// 切换状态栏图片

        // 检测是否可以接受数据
        connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
            QByteArray data = m_tcp->readAll();
            ui->record->append("客户端 : " + data);
        });

        // 断开连接
        connect(m_tcp,&QTcpSocket::disconnected ,this,[=](){
            m_tcp->close();
            m_tcp->deleteLater(); // 封装 delete
            m_status->setPixmap(QPixmap(":/red.png").scaled(20,20));

            ui->pushButton->setDisabled(true);

        });

        // 切换发送按钮状态
        ui->pushButton->setDisabled(false);

    });

    // 状态栏
    m_status = new QLabel;
    m_status->setPixmap(QPixmap(":/red.png").scaled(20,20));
    ui->statusBar->addWidget(new QLabel("连接状态: "));
    ui->statusBar->addWidget(m_status);
}

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

// 启动监听服务按钮
void MainWindow::on_setlisten_clicked()
{
    unsigned short port = ui->port->text().toUShort();
    m_s->listen(QHostAddress::Any ,port); // 设置监听
    ui->setlisten->setDisabled(true);

}

// 发送数据按钮
void MainWindow::on_pushButton_clicked()
{
    QString msg = ui->msg->toPlainText();
    m_tcp->write(msg.toUtf8()); // 输入客户端
    ui->record->append("服务器 : " + msg);

}

3.2 客户端

  1. 创建通信的套接字类 QTcpSocket 对象
  2. 使用服务器端绑定的 IP 和端口连接服务器 QAbstractSocket::connectToHost()
  3. 使用 QTcpSocket 对象和服务器进行通信

在这里插入图片描述

头文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QTcpSocket>
#include <QString>
#include <QLabel>
#include <QHostAddress>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void readData();


private slots:

    void on_pushButton_clicked();

    void on_connect_clicked();

    void on_disconnect_clicked();



private:
    Ui::MainWindow *ui;
    QTcpSocket* m_tcp;
    QLabel* m_status;
};

#endif // MAINWINDOW_H

源文件:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowIcon(QIcon(":/m4.ico"));
    setWindowTitle("客户端");
    ui->disconnect->setDisabled(true); // 初始时 ,断开连接按钮不可用

    ui->port->setText("30003"); // 默认端口
    ui->ip->setText("192.168.79.129"); // 默认ip
    ui->msg->setPlaceholderText("请输入指令");

    // 创建通信的套接字
    m_tcp =new QTcpSocket(this);

    // 接受到服务端消息
    connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
        QByteArray data = m_tcp->readAll();
        // ui->record->append("服务器 : " + data);
        // readData();
     });

    // 服务器端断开连接
    connect(m_tcp,&QTcpSocket::disconnected ,this,[=](){
        m_tcp->close();
        //m_tcp->deleteLater(); // 封装 delete
        m_status->setPixmap(QPixmap(":/red.png").scaled(20,20));
        ui->record->append("已经和服务器断开连接....");

        ui->connect->setDisabled(false);// 设置连接按钮不可用
        ui->disconnect->setDisabled(true);
    });

    // 检测连接状态
    connect(m_tcp,&QTcpSocket::connected ,this,[this](){
        m_status->setPixmap(QPixmap(":/green.png").scaled(20,20));
        ui->record->append("连接服务器成功...");

        ui->connect->setDisabled(true);
        ui->disconnect->setDisabled(false);
    });

    // 状态栏
    m_status = new QLabel;
    m_status->setPixmap(QPixmap(":/red.png").scaled(20,20));
    ui->statusBar->addWidget(new QLabel("连接状态: "));
    ui->statusBar->addWidget(m_status);
}

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

void MainWindow::readData()
{
    QByteArray byteArray;//临时变量,用来存储每次从机械臂读进来的数据
    byteArray.resize(this->m_tcp->bytesAvailable());//先设置每次读取的大小为可接收缓存的大小
    int byteRead = this->m_tcp->read(byteArray.data(), byteArray.size());//把缓存区所有数据读取回来,可以用readAll替代,但是现在用的函数可以判断读取结果

    if (byteRead == -1) {//读取错误
        ui->record->append("read data from robot error!") ;
        return;
    } else if (byteRead == 0) {//没读到东西
        ui->record->append("read data from robot error!") ;
        return;
    }
}

// 发送数据按钮
void MainWindow::on_pushButton_clicked()
{
    QString msg = ui->msg->toPlainText();
    m_tcp->write(msg.toUtf8()+"\n"); // 输入客户端
    ui->record->append("客户端 : " + msg);
    ui->msg->clear();
}

// 连接服务器按钮
void MainWindow::on_connect_clicked()
{
    QString ip = ui->ip->text();
    unsigned short port = ui->port->text().toUShort();
    m_tcp->connectToHost(QHostAddress(ip) , port);
}

// 断开连接
void MainWindow::on_disconnect_clicked()
{
    m_tcp->close();
    ui->disconnect->setDisabled(true);
    ui->connect->setDisabled(false);
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 12:16:32  更:2022-05-10 12:19:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:54:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码