Qt下的基本TCP网络通信流程

给大家讲一下如何使用Qt相关类的进行TCP通信。前置知识:c++基础、qt基础、网络基础

使用Qt提供的类进行基于TCP的套接字通信需要用到两个类:

QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接。
QTcpSocket:通信的套接字类,客户端、服务器端都需要使用。
这两个套接字通信类都属于网络模块network。所以需要加入对应的network模块。

image-20230814203032513

其实无论哪个语言,通信的流程都是大差不差的

1.QTcpServer

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

参数:
address:通过类QHostAddress可以封装IPv4、IPv6格式的IP地址,QHostAddress::Any表示自动绑定
port:如果指定为0表示随机绑定一个可用端口。
返回值:绑定成功返回true,失败返回false

得到和客户端建立连接之后用于通信的QTcpSocket套接字对象,它是QTcpServer的一个子对象,当QTcpServer对象析构的时候会自动析构这个子对象,当然也可自己手动析构,建议用完之后自己手动析构这个通信的QTcpSocket对象。

QTcpSocket *QTcpServer::nextPendingConnection();

如果了解Linux网络编程的话就会理解,其实这就是相当于accept函数,返回一个socket

1.2信号

当接受新连接导致错误时,将发射如下信号。socketError参数描述了发生的错误相关的信息。

[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);

每次有新连接可用时都会发出 newConnection() 信号。

[signal] void QTcpServer::newConnection();

2.QTcpSocket

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

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框架维护的一块内存。因此,调用了发送函数数据不一定会马上被发送到网络中,调用了接收函数也不是直接从网络中接收数据,关于底层的相关操作是不需要使用者来维护的。这其实是与Linux类似的,也是维护一片缓冲区内存用于通信。

接收数据

// 指定可接收的最大字节数 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.通信流程

3.1服务器端

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

3.1.2代码片段

服务器端如图

image-20230814205236113

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

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

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_setlisten_clicked();

    void on_sendmsg_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);
    ui->port->setText("9527");
    //实例化
    m_s = new QTcpServer(this);

    connect(m_s,&QTcpServer::newConnection,this,[=](){
        m_tcp = m_s->nextPendingConnection();
        //修改图片
        m_status->setPixmap(QPixmap(":/OIP-C.jpg").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(":/th.jpg").scaled(20,20));
        });

    });


    //状态栏
    m_status = new QLabel;
    m_status->setPixmap(QPixmap(":/th.jpg").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_sendmsg_clicked()
{
    QString msg = ui->msg->toPlainText();
    m_tcp->write(msg.toUtf8());
    ui->record->append("服务端说: "+msg);
}

3.2客户端

客户端与服务器端其实差不多,稍微修改即可如图

image-20230814205518856

3.2.1流程

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

3.2.2代码

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
#include <QPixmap>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:

    void on_sendmsg_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"

#include <QHostAddress>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->port->setText("9527");
    ui->ip->setText("127.0.0.1");
    setWindowTitle("客户端");
    ui->disconnect->setDisabled(true);
    //实例化
    m_tcp = new QTcpSocket(this);

    connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
        QByteArray data = m_tcp->readAll();
        ui->record->append("服务端发来:"+data);
    });


    connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
        ui->connect->setDisabled(false);
        ui->disconnect->setEnabled(false);
        m_tcp->close();
        //m_tcp->deleteLater();     //封装后的delete
        m_status->setPixmap(QPixmap(":/th.jpg").scaled(20,20));
        ui->record->append("连接断开.....");
    });

    connect(m_tcp,&QTcpSocket::connected,this,[=](){
        ui->connect->setDisabled(true);
        ui->disconnect->setEnabled(true);
        m_status->setPixmap(QPixmap(":/OIP-C.jpg").scaled(20,20));
        ui->record->append("连接成功.....");
    });


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

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

void MainWindow::on_sendmsg_clicked()
{
    QString msg = ui->msg->toPlainText();
    m_tcp->write(msg.toUtf8());
    ui->record->append("客户端说: "+msg);
}


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->connect->setDisabled(true);
    ui->disconnect->setEnabled(false);
}

下面是运行图片

image-20230814205919693

posted @ 2023-08-14 21:01  桂洛克船长  阅读(57)  评论(1编辑  收藏  举报