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

嵌入式学习-QT-Day09

嵌入式学习-QT-Day09

九、网络

1、基础概念

1.1 TCP/UDO

1.2 IP地址与端口号

2、准备工作

3、相关函数

九、网络

1、基础概念

1.1 TCP/UDP

TCP/UDP

UDP TCP 协议相同点:都存在于传输层,全双工通信

TCP:全双工通信、面向连接、可靠

TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。

高可靠原因:1. 三次握手、四次挥手

2. 序列号和应答机制

3. 超时,错误重传机制

4. 拥塞控制、流量控制(滑动窗口)

适用场景

适合于对传输质量要求较高的通信

在需要可靠数据传输的场合,通常使用TCP协议

MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议

UDP:全双工通信、面向无连接、不可靠

UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。

适用场景

发送小尺寸数据(如对DNS服务器进行IP地址查询时)

适合于广播/组播式通信中。

MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议

1.2 IP地址与端口号

IP

●IP地址是Internet中主机的标识

●Internet中的主机要与别的机器通信必须具有一个IP地址

●IP地址为32位(IPv4)或者128位(IPv6)

●每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由 

●表示形式:常用点分十进制形式,如202.38.64.10,最后都会转换为一个32位的无符号整数。

端口号

●为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分

●TCP端口号与UDP端口号独立

●端口用两个字节来表示    2byte

端口:1~1023是被占用的。

动态或私有端口:49152~65535 --固定某些服务使用--

建议使用2000以上,非豹子号的端口号。本次授课使用8887。

2、准备工作

与数据库编程一样,Qt的网络功能需要在.pro项目配置文件中增加network模块。

客户端:

服务端:

客户端:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    
private slots:
    void btnConnectClickedSlot();
    void btnSendClickedSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    connect(ui->pushButtonConnect,SIGNAL(clicked()),
            this,SLOT(btnConnectClickedSlot()));

    connect(ui->pushButtonSend,SIGNAL(clicked()),
            this,SLOT(btnSendClickedSlot()));
}

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

void Dialog::btnConnectClickedSlot()
{

}

void Dialog::btnSendClickedSlot()
{

}

网络编程主要用到两个类:

  • QTcpServer

表示一个基于TCP的服务器,需要注意的是,此类直接继承QObject类,不继承QIODevice类,因此不具备任何的IO能力。

  • QTcpSocket

表示一个基于TCP的Socket连接,间接继承了QIODevice类,因此此类对象可以进行IO读写。

3、相关函数

服务器

// 构造函数 堆内存开辟
QTcpServer::​QTcpServer(QObject * parent = 0)
// 开启监听服务,等待客户端发起连接
// 参数1:监听来源(那个网段的IP地址),默认值表示不加任何限制
// 参数2:服务器所占用的端口号,默认值0表示随机选取。本次使用8887
bool QTcpServer::​listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
// 判断监听是否已开启
bool QTcpServer::​isListening() const
// 关闭
void QTcpServer::​close()

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpServer>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpServer *server;
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    server = new QTcpServer(this);

    // 开启监听
    bool result = server->listen(QHostAddress::Any,8887);
    if(!result)
    {
        ui->textBrowser->append("监听失败!!");
        return;
    }
    ui->textBrowser->append("监听开启成功,端口号为:8887");
}

Dialog::~Dialog()
{
    // 关闭监听
    if(server->isListening())
    {
        server->close();
    }
    delete ui;
}

编写客户端:

// 构造函数 堆区创建
QTcpSocket::​QTcpSocket(QObject * parent = 0)

// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:打开模式
void QAbstractSocket::​connectToHost(
    const QString & hostName, 
    quint16 port, 
    OpenMode openMode = ReadWrite)[virtual]

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpSocket>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpSocket *socket;

private slots:
    void btnConnectClickedSlot();
    void btnSendClickedSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    connect(ui->pushButtonConnect,SIGNAL(clicked()),
            this,SLOT(btnConnectClickedSlot()));

    connect(ui->pushButtonSend,SIGNAL(clicked()),
            this,SLOT(btnSendClickedSlot()));

    socket = new QTcpSocket(this);
}

Dialog::~Dialog()
{
    // 如果数据流处于打开状态
    if(socket->isOpen())
    {
        // 如果打开则关闭
        socket->close();
    }
    delete ui;
}

void Dialog::btnConnectClickedSlot()
{
    QString ip = ui->lineEditIp->text();
    int port = ui->spinBox->value();

    // 建立连接
    socket->connectToHost(ip,port);
}

void Dialog::btnSendClickedSlot()
{

}

再回到服务器:

我们怎么直到,是否有客户端连接了那?

所有通知类的,我们应该第一时间想到的就是信号槽。

// 每当有新的连接可用时,就会发出此信号
void QTcpServer::​newConnection()[signal]

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpServer>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpServer *server;

private slots:
    void newConnectionSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    server = new QTcpServer(this);

    // 开启监听
    bool result = server->listen(QHostAddress::Any,8887);
    if(!result)
    {
        ui->textBrowser->append("监听失败!!");
        return;
    }
    connect(server,SIGNAL(newConnection()),
            this,SLOT(newConnectionSlot()));
    ui->textBrowser->append("监听开启成功,端口号为:8887");
}

Dialog::~Dialog()
{
    // 关闭监听
    if(server->isListening())
    {
        server->close();
    }
    delete ui;
}

void Dialog::newConnectionSlot()
{
    ui->textBrowser->append("新连接来啦!!!!");
}

客户端:

// 连接成功的信号
void QAbstractSocket::​connected()[signal]
// 断开连接的信号
void QAbstractSocket::​disconnected()[signal]

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    connect(ui->pushButtonConnect,SIGNAL(clicked()),
            this,SLOT(btnConnectClickedSlot()));

    connect(ui->pushButtonSend,SIGNAL(clicked()),
            this,SLOT(btnSendClickedSlot()));

    socket = new QTcpSocket(this);
}

Dialog::~Dialog()
{
    // 如果数据流处于打开状态
    if(socket->isOpen())
    {
        // 如果打开则关闭
        socket->close();
    }
    delete ui;
}

void Dialog::btnConnectClickedSlot()
{
    QString ip = ui->lineEditIp->text();
    int port = ui->spinBox->value();

    // 建立连接
    socket->connectToHost(ip,port);

    connect(socket,SIGNAL(connected()),
            this,SLOT(connectedSlot()));

    connect(socket,SIGNAL(disconnected()),
            this,SLOT(disConnectedSlot()));
}

void Dialog::btnSendClickedSlot()
{

}


// 连接成功
void Dialog::connectedSlot()
{
    // 屏蔽连接按钮
    ui->pushButtonConnect->setEnabled(false);
    ui->pushButtonConnect->setText("已连接");

    // 释放发送按钮
    ui->pushButtonSend->setEnabled(true);
}

// 断开连接
void Dialog::disConnectedSlot()
{
    // 释放连接按钮
    ui->pushButtonConnect->setEnabled(true);
    ui->pushButtonConnect->setText("连接");

    // 屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
}

// 返回与客户端连接的QTcpSocket对象
QTcpSocket * QTcpServer::​nextPendingConnection()[virtual]
// 获取对面(客户端)的IP地址
// 返回值为IP地址的封装类
QHostAddress QAbstractSocket::​peerAddress() const
// 转换为IP地址字符串,在有的计算机中会自动增加一个前缀
QString QHostAddress::​toString() const
// 返回对面的(客户端)端口号
quint16 QAbstractSocket::​peerPort() const

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpServer *server;
    QTcpSocket *socket = NULL;  // 通信对象(这样只能存一个)

private slots:
    void newConnectionSlot();
    void disconnectedSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    server = new QTcpServer(this);

    // 开启监听
    bool result = server->listen(QHostAddress::Any,8887);
    if(!result)
    {
        ui->textBrowser->append("监听失败!!");
        return;
    }
    connect(server,SIGNAL(newConnection()),
            this,SLOT(newConnectionSlot()));
    ui->textBrowser->append("监听开启成功,端口号为:8887");
}

Dialog::~Dialog()
{
    // 关闭监听
    if(server->isListening())
    {
        server->close();
    }
    delete ui;
}

void Dialog::newConnectionSlot()
{
    // 如果不是第一次连接,就踢掉上一个人
    if(socket != NULL)
    {
        // 踢掉上一个人
        socket->close();
    }

    // 保存当前连接对象
    socket = server->nextPendingConnection();

    // 断开检测信号槽
    connect(socket,SIGNAL(disconnected()),
            this,SLOT(disconnectedSlot()));

    // 获取对面的ip地址
    QString ip = socket->peerAddress().toString();
    // 获取对面的端口号
    quint16 port = socket->peerPort();

    // 字符串拼接
    ip.prepend("新连接来了!").append(":").append(QString::number(port));

    ui->textBrowser->append(ip);
}

void Dialog::disconnectedSlot()
{
    ui->textBrowser->append("老连接走了!!");
}

数据流发送与处理的第一种方式:

// 构造函数
// 参数是Qt的读写类,可以是QFile,也可以是QTcpSocket。。。。。。
QTextStream::​QTextStream(QIODevice * device)
// 输出字符串内容,支持链式调用
QTextStream &	operator<<(const QString & string)
// 有数据可读时发射
void QIODevice::​readyRead()[SIGNAL]
// 读取最大长度为maxlen个QChar的内容,返回值为读取的字符串
QString QTextStream::​read(qint64 maxlen)
// 读取所有字符
QString QTextStream::​readAll()
// 一次读取一行文本
// 参数为一行文本的最大字符数
QString QTextStream::​readLine(qint64 maxlen = 0)

客户端:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpSocket>
#include <QTextStream>  // 文本流类
#include <QMessageBox>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpSocket *socket;

private slots:
    void btnConnectClickedSlot();
    void btnSendClickedSlot();
    void connectedSlot();
    void disConnectedSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    connect(ui->pushButtonConnect,SIGNAL(clicked()),
            this,SLOT(btnConnectClickedSlot()));

    connect(ui->pushButtonSend,SIGNAL(clicked()),
            this,SLOT(btnSendClickedSlot()));

    socket = new QTcpSocket(this);
}

Dialog::~Dialog()
{
    // 如果数据流处于打开状态
    if(socket->isOpen())
    {
        // 如果打开则关闭
        socket->close();
    }
    delete ui;
}

void Dialog::btnConnectClickedSlot()
{
    QString ip = ui->lineEditIp->text();
    int port = ui->spinBox->value();

    // 建立连接
    socket->connectToHost(ip,port);

    connect(socket,SIGNAL(connected()),
            this,SLOT(connectedSlot()));

    connect(socket,SIGNAL(disconnected()),
            this,SLOT(disConnectedSlot()));
}

void Dialog::btnSendClickedSlot()
{
    QString text = ui->lineEditSend->text();
    if(text == "")
    {
        QMessageBox::information(this,"提示","请输入发送的内容!!");
        return;
    }

    // QTextStream文本流(方法一)
    // QTextStream直接使用Unicode编码,适合Qt和Qt之间通信
    // 可以简化文本的读写操作
    QTextStream output(socket);
    output << text;
}


// 连接成功
void Dialog::connectedSlot()
{
    // 屏蔽连接按钮
    ui->pushButtonConnect->setEnabled(false);
    ui->pushButtonConnect->setText("已连接");

    // 释放发送按钮
    ui->pushButtonSend->setEnabled(true);
}

// 断开连接
void Dialog::disConnectedSlot()
{
    // 释放连接按钮
    ui->pushButtonConnect->setEnabled(true);
    ui->pushButtonConnect->setText("连接");

    // 屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
}

服务端

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTextStream>  // 文本流

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpServer *server;
    QTcpSocket *socket = NULL;  // 通信对象(这样只能存一个)

private slots:
    void newConnectionSlot();
    void disconnectedSlot();
    void readyReadSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    server = new QTcpServer(this);

    // 开启监听
    bool result = server->listen(QHostAddress::Any,8887);
    if(!result)
    {
        ui->textBrowser->append("监听失败!!");
        return;
    }
    connect(server,SIGNAL(newConnection()),
            this,SLOT(newConnectionSlot()));
    ui->textBrowser->append("监听开启成功,端口号为:8887");
}

Dialog::~Dialog()
{
    // 关闭监听
    if(server->isListening())
    {
        server->close();
    }
    delete ui;
}

void Dialog::newConnectionSlot()
{
    // 如果不是第一次连接,就踢掉上一个人
    if(socket != NULL)
    {
        // 踢掉上一个人
        socket->close();
    }

    // 保存当前连接对象
    socket = server->nextPendingConnection();

    // 断开检测信号槽
    connect(socket,SIGNAL(disconnected()),
            this,SLOT(disconnectedSlot()));

    // 连接数据流处理信号槽
    connect(socket,SIGNAL(readyRead()),
            this,SLOT(readyReadSlot()));

    // 获取对面的ip地址
    QString ip = socket->peerAddress().toString();
    // 获取对面的端口号
    quint16 port = socket->peerPort();

    // 字符串拼接
    ip.prepend("新连接来了!").append(":").append(QString::number(port));

    ui->textBrowser->append(ip);
}

void Dialog::disconnectedSlot()
{
    ui->textBrowser->append("老连接走了!!");
}

void Dialog::readyReadSlot()
{
    // 创建文本流对象
    QTextStream input(socket);

    // 读取内容
    QString text = input.read(128);

    // 显示
    ui->textBrowser->append(text);
}

第二种方式:

客户端

dialog.cpp

服务器:

dialog.cpp


http://www.kler.cn/a/454229.html

相关文章:

  • 理解神经网络
  • Java全栈项目 - 学生竞赛管理平台
  • ArkTs组件(2)
  • 软考和 PMP 哪个含金量更高点?
  • YOLOv9-0.1部分代码阅读笔记-benchmarks.py
  • 学习threejs,THREE.RingGeometry 二维平面圆环几何体
  • Linux 简单命令总结
  • Android笔记(四十):ViewPager2嵌套RecyclerView滑动冲突进一步解决
  • 企业如何搭建安全的跨网文件安全交换管理系统
  • 人工智能的视觉天赋:一文读懂卷积神经网络
  • Unity 应用程序中集成 Web 浏览器功能
  • linux、mysql日常工作备忘录
  • Redis 应用场景深度探索
  • docker hub 官方redis镜像使用方法
  • 抓取手机HCI日志
  • MR眼镜如何接管我们的手机生活
  • pikachu靶场RCE漏洞
  • 以太网帧结构
  • JS获取URL中参数值的4种方法
  • 001集—— 块表、快表记录、块参照详解 —— ifox CADc#二次开发
  • Docker下TestHubo安装配置指南
  • 快速理解24种设计模式
  • Ubuntu 24.04 APT源配置详解
  • 【Linux】top中的VIRT=RES+SHR的等式是否成立?
  • QT多媒体开发(三):使用QMediaPlayer播放视频
  • 学习记录—正则表达式-基本语法