关于TCP通信的学习和应用案例

news2024/9/21 19:08:12

记录学习TCP通信的过程,包括理论知识、在Qt中建立TCP服务端和客户端,并附上源代码。由于最近的项目中也使用到了海康VisionMaster软件,可以将其作为服务端,用Qt写的TCP客户端和其进行通信测试,方便调试。

目录

  • 1.关于TCP理论知识
    • 1.1 TCP如何保证可靠性
    • 1.2 简述下TCP建立连接和断开连接的过程
    • 1.3 TCP的模型
    • 1.4 HTTP和HTTPS的区别,以及HTTPS有什么优缺点
  • 2.Qt中TCP通信
    • 2.1 QTcpServer
    • 2.2 QTcpSocket
    • 2.3 使用多线程进行网络通信
    • 2.4 源码:TCP服务端和TCP客户端
      • TCP客户端
      • TCP服务端
      • 使用
  • 3.使用海康VisionMaster和TCP客户端进行通信

1.关于TCP理论知识

参考:https://www.nowcoder.com/tutorial/93/e1b14ab2b40a4ef98d9e55830eb48d66

  socket(套接字):四元组:客户端ip+端口号port+服务端ip+端口号port,保证这是绝对唯一的连接(这是一个整体)。端口号最多有65535个。

1.1 TCP如何保证可靠性

  • (1)序列号、确认应答、超时重传
      数据到达接收方,接收方需要发出一个确认应答,表示已经收到该数据段,并且确认序号会说明了它下一次需要接收的数据序列号。如果发送发迟迟未收到确认应答,那么可能是发送的数据丢失,也可能是确认应答丢失,这时发送方在等待一定时间后会进行重传。这个时间一般是2*RTT(报文段往返时间)+一个偏差值
  • (2)窗口控制与高速重发控制/快速重传(重复确认应答)
      TCP会利用窗口控制来提高传输速度,意思是在一个窗口大小内,不用一定要等到应答才能发送下一段数据,窗口大小就是无需等待确认而可以继续发送数据的最大值。如果不使用窗口控制,每一个没收到确认应答的数据都要重发。
      使用窗口控制,如果数据段1001-2000丢失,后面数据每次传输,确认应答都会不停地发送序号为1001的应答,表示我要接收1001开始的数据,发送端如果收到3次相同应答,就会立刻进行重发;但还有种情况有可能是数据都收到了,但是有的应答丢失了,这种情况不会进行重发,因为发送端知道,如果是数据段丢失,接收端不会放过它的,会疯狂向它提醒…
  • (3)拥塞控制
      如果把窗口定的很大,发送端连续发送大量的数据,可能会造成网络的拥堵(大家都在用网,你在这狂发,吞吐量就那么大,当然会堵),甚至造成网络的瘫痪。所以TCP在为了防止这种情况而进行了拥塞控制
      慢启动:定义拥塞窗口,一开始将该窗口大小设为1,之后每次收到确认应答(经过一个rtt),将拥塞窗口大小*2。
      拥塞避免:设置慢启动阈值,一般开始都设为65536。拥塞避免是指当拥塞窗口大小达到这个阈值,拥塞窗口的值不再指数上升,而是加法增加(每次确认应答/每个rtt,拥塞窗口大小+1),以此来避免拥塞。
    将报文段的超时重传看做拥塞,则一旦发生超时重传,我们需要先将阈值设为当前窗口大小的一半,并且将窗口大小设为初值1,然后重新进入慢启动过程。
      快速重传:在遇到3次重复确认应答(高速重发控制)时,代表收到了3个报文段,但是这之前的1个段丢失了,便对它进行立即重传。
    然后,先将阈值设为当前窗口大小的一半,然后将拥塞窗口大小设为慢启动阈值+3的大小。
      这样可以达到:在TCP通信时,网络吞吐量呈现逐渐的上升,并且随着拥堵来降低吞吐量,再进入慢慢上升的过程,网络不会轻易的发生瘫痪。

1.2 简述下TCP建立连接和断开连接的过程

客户端包括:
①应用层
②传输控制层:TCP、UDP。TCP是面向连接的、可靠的传输。TCP包括:三次握手、数据传输、四次分手
③网络层
④链路层
⑤物理层

  • TCP建立连接和断开连接的过程
    在这里插入图片描述
  • 三次握手
       三次握手是建立连接,开启socket。
      1.Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
      2.Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
      3.Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
    在这里插入图片描述
  • 四次挥手
      四次分手是断开连接,进行资源的释放。
      由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

1.数据传输结束后,客户端的应用进程发出连接释放报文段,并停止发送数据,客户端进入FIN_WAIT_1状态,此时客户端依然可以接收服务器发送来的数据。
2.服务器接收到FIN后,发送一个ACK给客户端,确认序号为收到的序号+1,服务器进入CLOSE_WAIT状态。客户端收到后进入FIN_WAIT_2状态。
3.当服务器没有数据要发送时,服务器发送一个FIN报文,此时服务器进入LAST_ACK状态,等待客户端的确认
4.客户端收到服务器的FIN报文后,给服务器发送一个ACK报文,确认序列号为收到的序号+1。此时客户端进入TIME_WAIT状态,等待2MSL(MSL:报文段最大生存时间),然后关闭连接。

1.3 TCP的模型

四层TCP/IP模型如下:
在这里插入图片描述

1.4 HTTP和HTTPS的区别,以及HTTPS有什么优缺点

  • HTTP协议和HTTPS协议区别如下
      1.HTTP协议是以明文的方式在网络中传输数据,而HTTPS协议传输的数据则是经过TLS加密后的,HTTPS具有更高的安全性
      2.HTTPS在TCP三次握手阶段之后,还需要进行SSL 的handshake,协商加密使用的对称加密密钥
      3.HTTPS协议需要服务端申请证书,浏览器端安装对应的根证书
      4.HTTP协议端口是80,HTTPS协议端口是443

  • HTTPS优点
      1.HTTPS传输数据过程中使用密钥进行加密,所以安全性更高
      2.HTTPS协议可以认证用户和服务器,确保数据发送到正确的用户和服务器

  • HTTPS缺点
      1.HTTPS握手阶段延时较高:由于在进行HTTP会话之前还要进行SSL握手,因此HTTPS协议握手阶段延时增加
      2.HTTPS部署成本高:一方面HTTPS协议需要使用证书来验证自身的安全性,所以需要购买CA证书;另一方面由于采用HTTPS协议需要进行加解密的计算,占用CPU资源较多,需要的服务器配置或数目高。


在这里插入图片描述
  三次握手、四次握手都是传输控制层的,属于内核,我们写代码时不需要考虑这些。
在这里插入图片描述
  什么是TCP?
TCP是面向连接的可靠的传输控制协议。
  使用过程:
通过程序从客户端C向服务端S发送请求syn,服务端S收到请求后对客户端C进行回应,发送syn+ack,客户端C向服务端S发送ack表示收到回应。由此建立客户端C和服务端S的联系,建立之后在服务端S建立一个内存空间buffer,在客户端C建立一个内存空间buffer,由程序控制两个内存空间之间的数据传输。

在这里插入图片描述
程序不能直接控制这两个内存空间,需要通过socket来处理。
什么是socket?
又称套接字、插座。
所谓套接字,即有套接和被套接,插座有插头和插座,即2组,IP+Port(IP地址+端口号)
2组组成:ip:port + ip:port —— 4元组
哪怕其中只有一个改变了,也是独立的一组


2.Qt中TCP通信

https://subingwen.cn/linux/socket/#4-TCP%E9%80%9A%E4%BF%A1%E6%B5%81%E7%A8%8B

2.1 QTcpServer

在这里插入图片描述
TCP服务器端:首先创建套接字socket(),然后绑定bind(),设置监听listen(),然后等待客户端的连接accept(),连接成功之后,接收数据rev()和发送数据send(),最后关闭套接字close()

TCP客户端:首先创建套接字socket(),然后连接服务器connect(),连接之后和服务器进行通信,发送数据send()和接收数据rev(),最后关闭套接字close()

在这里插入图片描述

2.2 QTcpSocket

在这里插入图片描述

2.3 使用多线程进行网络通信

示例场景:TCP客户端和服务端,通过子线程处理将客户端的文件发送给服务器

https://www.bilibili.com/video/BV1LB4y1F7P7/?p=10&spm_id_from=pageDriver&vd_source=858585879400a2acad4b4d9a0283f25d

2.4 源码:TCP服务端和TCP客户端

环境:Ubuntu16.04 + QT5.12.9 + CMake3.21

TCP客户端

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(tcp_client LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt5 COMPONENTS Widgets REQUIRED Network)

if(ANDROID)
  add_library(tcp_client SHARED
    main.cpp
    tcpclient.cpp
    tcpclient.h
    tcpclient.ui
    resources.qrc
  )
else()
  add_executable(tcp_client
    main.cpp
    tcpclient.cpp
    tcpclient.h
    tcpclient.ui
    resources.qrc
  )
endif()

target_link_libraries(tcp_client PRIVATE Qt5::Widgets Qt5::Network)

tcpclient.ui
在这里插入图片描述
main.cpp

#include "tcpclient.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TcpClient w;
    w.show();
    w.setWindowTitle("TCP - 客户端");
    return a.exec();
}

tcpclient.h

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui {
class TcpClient;
}
QT_END_NAMESPACE

class TcpClient : public QMainWindow
{
    Q_OBJECT

public:
    TcpClient(QWidget *parent = nullptr);
    ~TcpClient();

private slots:
    void on_pbn_connect_clicked();

    void on_pbn_disconnect_clicked();

    void on_pbn_send_message_clicked();

private:
    Ui::TcpClient *ui;

    QTcpSocket *tcp_{ nullptr };
    QLabel *status_{ nullptr };
};
#endif // TCPCLIENT_H

tcpclient.cpp

#include "tcpclient.h"
#include "./ui_tcpclient.h"

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

    ui->le_port->setText("8000");
    ui->le_ip->setText("127.0.0.1");
    ui->pbn_disconnect->setEnabled(false);

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

    // 检测服务器是否回复了数据
    connect(tcp_, &QTcpSocket::readyRead, [=] {
        // 接收服务器发送的数据
        QByteArray recv_msg = tcp_->readAll();
        ui->txe_message->append("服务器Say: " + recv_msg);
    });

    // 检测是否和服务器是否连接成功了
    connect(tcp_, &QTcpSocket::connected, this, [=]() {
        ui->txe_message->append("恭喜, 连接服务器成功!!!");
        status_->setPixmap(QPixmap(":/resources/connect.png").scaled(20, 20));
        ui->pbn_connect->setEnabled(false);
        ui->pbn_disconnect->setEnabled(true);
    });

    // 检测服务器是否和客户端断开了连接
    connect(tcp_, &QTcpSocket::disconnected, this, [=]() {
        ui->txe_message->append("服务器已经断开了连接...");
        ui->pbn_connect->setEnabled(true);
        ui->pbn_disconnect->setEnabled(false);
        status_->setPixmap(
            QPixmap(":/resources/disconnect.png").scaled(20, 20));
    });

    // 设置连接状态的状态栏
    status_ = new QLabel(this);
    status_->setPixmap(QPixmap(":/resources/disconnect.png").scaled(20, 20));
    ui->statusbar->addWidget(new QLabel("连接状态:"));
    ui->statusbar->addWidget(status_);
}

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

void TcpClient::on_pbn_connect_clicked()
{
    QString ip = ui->le_ip->text();
    unsigned short port = ui->le_port->text().toInt();
    // 连接服务器
    tcp_->connectToHost(QHostAddress(ip), port);
    ui->pbn_connect->setEnabled(false);
    ui->pbn_disconnect->setEnabled(true);
}

void TcpClient::on_pbn_disconnect_clicked()
{
    tcp_->close();
    ui->pbn_connect->setEnabled(true);
    ui->pbn_disconnect->setEnabled(false);
}

void TcpClient::on_pbn_send_message_clicked()
{
    QString send_msg = ui->txe_send_message->toPlainText();
    tcp_->write(send_msg.toUtf8());
    ui->txe_message->append("客户端Say: " + send_msg);
    ui->txe_send_message->clear();
}

运行效果:
在这里插入图片描述

TCP服务端

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)

project(tcp_server LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt5 COMPONENTS Widgets REQUIRED Network)

if(ANDROID)
  add_library(tcp_server SHARED
    main.cpp
    tcpserver.cpp
    tcpserver.h
    tcpserver.ui
    resources.qrc
  )
else()
  add_executable(tcp_server
    main.cpp
    tcpserver.cpp
    tcpserver.h
    tcpserver.ui
    resources.qrc
  )
endif()

target_link_libraries(tcp_server PRIVATE Qt5::Widgets Qt5::Network)

tcpserver.ui
在这里插入图片描述
main.cpp

#include "tcpserver.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TcpServer w;
    w.show();
    w.setWindowTitle("TCP - 服务器");
    return a.exec();
}

tcpserver.h

#ifndef TCPSERVER_H
#define TCPSERVER_H

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

QT_BEGIN_NAMESPACE
namespace Ui {
class TcpServer;
}
QT_END_NAMESPACE

class TcpServer : public QMainWindow
{
    Q_OBJECT

public:
    TcpServer(QWidget *parent = nullptr);
    ~TcpServer();

private slots:
    void on_pbn_set_listen_clicked();

    void on_pbn_send_data_clicked();

private:
    Ui::TcpServer *ui;

    QTcpServer *server_{ nullptr };
    QTcpSocket *tcp_{ nullptr };
    QLabel *status_{ nullptr };
};
#endif // TCPSERVER_H

tcpserver.cpp

#include "tcpserver.h"
#include "./ui_tcpserver.h"

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

    ui->le_port->setText("8899");

    // 第一步:创建监听的服务对象
    server_ = new QTcpServer(
        this); // 指定实例化父类this,即QMainWindow,待页面析构时,server_也被析构

    // 第三步:通过 QTcpServer::newConnection()信号检测是否有新的客户端连接
    // 如果有新的客户端连接调用 QTcpSocket *QTcpServer::nextPendingConnection()
    // 得到通信的套接字对象
    connect(server_, &QTcpServer::newConnection, this, [=]() {
        tcp_ = server_->nextPendingConnection();
        ui->txe_record->append("成功和客户端建立了新的连接...");
        status_->setPixmap(QPixmap(":/resources/connect.png").scaled(20, 20));

        // 检测是否有客户端数据
        connect(tcp_, &QTcpSocket::readyRead, this, [=]() {
            // 接收数据
            QByteArray data = tcp_->readAll();
            ui->txe_record->append("客户端Say:" + data);
        });

        // 检测客户端是否断开了连接
        connect(tcp_, &QTcpSocket::disconnected, this, [=]() {
            ui->txe_record->append("客户端已经断开了连接...");
            tcp_->deleteLater();
            status_->setPixmap(
                QPixmap(":/resources/disconnect.png").scaled(20, 20));
        });
    });

    // 设置连接状态的状态栏
    status_ = new QLabel(this);
    status_->setPixmap(QPixmap(":/resources/disconnect.png").scaled(20, 20));
    ui->statusbar->addWidget(new QLabel("连接状态:"));
    ui->statusbar->addWidget(status_);
}

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

// 第二步:通过 QTcpServer 对象设置监听,即:QTcpServer::listen()
void TcpServer::on_pbn_set_listen_clicked()
{
    unsigned short port = ui->le_port->text().toUShort();
    // 设置服务器监听
    server_->listen(QHostAddress::Any, port);
    ui->pbn_set_listen->setEnabled(false);
}

void TcpServer::on_pbn_send_data_clicked()
{
    // 将txe_send_message中输入内容转为纯文本的QString形式
    QString msg = ui->txe_send_message->toPlainText();
    // 将QSting类型转为QByteArray类型
    tcp_->write(msg.toUtf8());
    ui->txe_record->append("服务端Say:" + msg);
    ui->txe_send_message->clear();
}

运行效果:
在这里插入图片描述

使用

1.打开TCP-服务端TCP-客户端
在这里插入图片描述
2.点击TCP-服务器启动监听服务器,打开端口8899的监听,修改TCP-客户端服务端端口,改为8899,点击连接服务器。完成客户端和服务端的连接。
在这里插入图片描述
3.客户端和服务端之间可进行通讯。
在这里插入图片描述

3.使用海康VisionMaster和TCP客户端进行通信

1.首先查看当前设备IP地址
在这里插入图片描述
2.打开海康VisionMaster中通信管理
在这里插入图片描述
修改为当前本机IP地址,设置本地端口,使能打开TCP服务端0
在这里插入图片描述
3.运行TCP-客户端,修改服务器地址IP和端口,和VisionMaster中一致,点击连接服务端,即可看到连接服务端成功。
在这里插入图片描述
4.测试海康VisionMaster和TCP客户端进行通信
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/138669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB APP 设计实践(一)UART通信(上篇)

引言UART通信属于异步串行通信&#xff0c;通信速率比较低&#xff0c;在一些速度要求不高的场合常用来作为多设备之间的控制与被控制方式。例如以UART串口通信作为上位机侧与运行设备之间的通信形式&#xff0c;实现上位机对设备的操控以及检测设备运行状态等。那么谈到了上位…

PyTorch实战1

传送门&#xff1a;蓝桥云课实验 目录1. 实验环境2. 实验目的3. 相关原理4. 实验步骤4.1 数据预处理4.1.1 对于类型变量的处理4.1.2 对于数值类型变量进行标准化4.1.3 数据集分割4.2 创建模型手写用Tensor运算的人工神经网络4.3 训练模型4.3.1 数据的分批次处理4.4 测试模型1. …

逆向-还原代码之eth (Arm 64)

// 源程序 #include <stdio.h> #define HIETH_SYSREG_BASE (0x101e0000) #define REG_RESET 0x01C // 外设控制寄存器(IP软复位控制) #define RESET_SHIFT 12 static void hieth_set_regbit(unsigned long addr, int bit, int shift) { unsigned long …

Practise test day16

一.单选 1.在关系型是数据库中&#xff0c;有两个不同的事务同时操作数据库中同一表的同一行&#xff0c;不会引起冲突的是&#xff1a;&#xff08;F&#xff09; A. 其中一个DELETE操作&#xff0c;一个是SELECT操作 B. 其中两个都是UPDATE C. 其中一个是SELECT&#xff…

ruoyi通过oauth对接pig实现sso流程讲解

1、时序图 2、流程解析 本流程是以使用Ruoyi对接Pig授权中心为例&#xff0c;进行讲解&#xff0c;其他网站的的oauth的原理都和这个一样&#xff0c;所以只要把这个流程搞懂了即可&#xff0c;接下来就按照真实的流程进行逐步解析。 2.1 第1步 用户还未登录&#xff0c;访问r…

浏览器Performance性能监控使用详解

文章目录1.Performance2.测试性能操作流程3.Performance检测结果详解区域1&#xff1a;controls【控制栏】区域2&#xff1a;overview【网页性能总览图】区域3&#xff1a;火焰图【各项指标的堆叠追踪可视化】区域4&#xff1a;统计汇总【以图表的形式汇总数据】4 其他监控性能…

BGP在数据中心的应用6——BGP在服务器上的应用

注&#xff1a; 本文根据《BGP in the Datacenter》整理&#xff0c;有兴趣和英文阅读能力的朋友可以直接看原文&#xff1a;https://www.oreilly.com/library/view/bgp-in-the/9781491983416/上一部分笔记请参考&#xff1a;https://blog.csdn.net/tushanpeipei/article/deta…

视频如何在线生成二维码?视频转二维码的2种方法

现在很多小伙伴都喜欢将视频转二维码&#xff0c;通过这种方式来分享传递内容&#xff0c;那么如何将视频生成二维码更加的简单快捷呢&#xff1f;大家可以用一下小编分享的这款在线二维码生成工具来制作二维码&#xff0c;通过浏览器在线生成二维码&#xff0c;更加的简单快捷…

设计模式——迭代器模式

迭代器模式一、基本思想二、结构图三、代码一、基本思想 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式&#xff0c;其主要优点如下&#xff1a; 访问一个聚合对象的内容而无须暴露它的内部表示。遍…

【Kotlin】函数 ④ ( 匿名函数参数 | 匿名函数 it 关键字 )

文章目录一、匿名函数参数二、匿名函数 it 关键字一、匿名函数参数 匿名函数 可以不带参数 , 也可以带多个参数 ; 不带参数的匿名函数 : // 声明 函数类型 变量, 并为其赋值 匿名函数val helloFun: ()->String {"Hello World"}带参数的匿名函数 : 匿名函数 的 参…

安装一个Excel插件,轻松网罗50+主流数据库

电子表格软件&#xff08;Smartbi Spreadsheet&#xff09;是思迈特软件推出的企业报表产品&#xff0c;产品以“真Excel”为特色&#xff0c;只需要安装一个小小插件&#xff0c;就能解决Excel最头疼的数据连接和性能问题。电子表格软件的数据源范围涵盖了本地数据库、关系型数…

如何学习虚幻引擎的Blueprint?

&#xff08;虚幻学习路线-方法概括版&#xff09; 【写在前面】&#xff1a;本篇内容颇多&#xff0c;并不是一大堆全学&#xff0c;把这篇当成一个字典&#xff0c;选择性学习&#xff0c;缺哪学哪~ 一、Blueprint &#xff08;一&#xff09;什么是虚幻引擎的Blueprint&…

C++STL之list容器

一&#xff1a;list特性list为带哨兵位双向循环链表&#xff0c;支持任意位置的插入和删除。与&#xff08;array&#xff0c;vector&#xff0c;deque&#xff09;相比&#xff0c;list的移除元素效率更高。最大缺陷是不支持[]重载&#xff0c;不支持随机访问&#xff0c;只能…

基于 EventBridge API Destination 构建 SaaS 集成实践方案

作者&#xff1a;赵海 引言 事件总线 EventBridge 是阿里云提供的一款无服务器事件总线服务&#xff0c;支持阿里云服务、自定义应用、SaaS 应用以标准化、中心化的方式接入&#xff0c;并能够以标准化的 CloudEvents 1.0 协议在这些应用之间路由事件&#xff0c;帮助您轻松构…

Mockjs入门基础概念与使用

文章目录Mockjs入门使用1、概述2、安装引入3、语法规范3.1 数据模板定义规范&#xff08;DTD)3.1.1 DTD介绍3.1.2 规范实例演示字符串String数字Number布尔Boolean对象Object数组Array函数Function正则表达式RegExp3.2 数据占位符定义规范DPD4、关于Mock.mock()方法4.1 参数介绍…

RabbitMQ 常见面试题

RabbitMQ 常见面试题 1.为什么要用消息队列? (消息队列的应用场景?) 2.各种消息队列产品的比较? 3.消息队列的优点和缺点? 4.如何保证消息队列的高可用? 5.如何保证消息不丢失? 6.如何保证消息不被重复消费?(如何保证消息消费的幂等性&#xff09; 7.如何保证消息消费的…

PMP证书好考吗?

PMP 还是很好考的&#xff0c;各大机构 3A 的人也很多&#xff0c;我的备考经验分享给大家参考下&#xff0c;大家可以取长补短&#xff0c;找到适合自己的备考方法&#xff1a;一、复习计划的制定根据之前在培训班共同奋斗的小伙伴学习时间统计&#xff0c;平均每天的学习时间…

我的第一门编程语言

元旦节在家重温了一遍《三体》&#xff0c;看到下面一段描写&#xff1a;监听部的计算机系统也远比发射部庞大复杂&#xff0c;叶文洁第一次走进主机房时&#xff0c;看到一排阴极射线管显示屏&#xff0c;她惊奇地发现&#xff0c;屏幕上竟滚动着一排排程序代码&#xff0c;可…

KITTI评价指标学习

在pointpillars完成训练后,可以看到对于目标检测的评估,据了解,这是kitti的标准目标检测格式,所以了解了一下. 在generate label finished后,可以看到类别Car对应的AP(Average precision)有3个0.70,分别对应的是Kitti object detection中,被分为Easy, Moderate,和Hard三种级别物…

算法训练 —— 哈希

目录 1. LeetCode242. 有效字母的异位词 2. LeetCode349. 两个数组的交集 3. LeetCode350. 两个数组的交集II 4. LeetCode202. 快乐数 5. LeetCode1. 两数之和 1. LeetCode242. 有效字母的异位词 有效字母的异位词 本题的含义就是判断两个字符串是否相同&#xff1b; 我们…