项目-SERVER模块-Socket模块

Socket模块

  • 一、Socket模块是什么?
  • 二、代码实现
    • 1.成员变量
    • 2.构造、析构函数
    • 3.获取套接字文件描述符
    • 4.创建套接字
    • 5.绑定地址信息
    • 6.开始监听连接请求
    • 7.向服务器发起连接
    • 8.获取新连接
    • 9.接收数据
    • 10.非阻塞接收数据
    • 11.发送数据
    • 12.非阻塞发送数据
    • 13.关闭套接字
    • 14.创建一个服务端连接
    • 15.创建一个客户端连接
    • 16.设置套接字选项——开启地址端口重用
    • 17. 设置套接字阻塞属性——设置为非阻塞
    • 18.测试代码
    • 19.整体源代码


一、Socket模块是什么?

Socket模块是对套接字操作封装的⼀个模块,主要实现的socket的各项操作。
在这里插入图片描述

二、代码实现

1.成员变量

这行代码表示在类(class)的私有(private)部分声明了一个整型变量 _sockfd,用于存储某个类的套接字文件描述符。在类的私有部分声明的成员只能在该类的成员函数内部访问,外部无法直接访问。

private:
    int _sockfd;

2.构造、析构函数

这段代码展示了一个名为Socket的类的构造函数和析构函数的定义:

  1. Socket() : _sockfd(-1) {}: 这是一个无参数的构造函数,用于初始化Socket类的对象。在这个构造函数中,通过初始化列表将_sockfd成员变量设置为-1,表示套接字文件描述符的初始值为-1。

  2. Socket(int sockfd) : _sockfd(sockfd) {}: 这是一个带有整型参数的构造函数,用于初始化Socket类的对象并指定套接字文件描述符的值。在这个构造函数中,通过初始化列表将_sockfd成员变量设置为传入的参数sockfd的值。

  3. ~Socket() { Close(); }: 这是Socket类的析构函数,用于释放资源和清理工作。在析构函数中调用了Close()函数,该函数应该是Socket类的一个成员函数,用于关闭套接字。通过在析构函数中调用Close()函数,确保在对象被销毁时及时关闭相关资源,避免资源泄漏。

public:
    Socket() : _sockfd(-1) {}
    Socket(int sockfd) : _sockfd(sockfd) {}
    ~Socket() { Close(); }

3.获取套接字文件描述符

这段代码定义了一个名为get_fd()的成员函数,用于获取类中私有成员变量_sockfd的值(套接字文件描述符)。该函数返回整型值,表示获取到的套接字文件描述符。

通过定义这样的成员函数,可以在类外部获取Socket类对象的套接字文件描述符,同时保持_sockfd作为私有成员的封装性。这样的设计方式遵循了面向对象编程的封装原则,将类的数据隐藏起来,只允许通过成员函数来进行访问和操作,从而提高了代码的安全性和可维护性。

    // 获取套接字文件描述符
    int get_fd() { return _sockfd; }

4.创建套接字

    // 创建套接字
    bool Create()
    {
        // 调用socket函数创建套接字
        // int socket(int domain, int type, int protocol)
        _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        // 检查套接字创建是否成功
        if (_sockfd < 0)
        {
            // 套接字创建失败时输出日志信息
            INF_LOG("Socket creation failed");
            return false;
        }

        // 套接字创建成功
        return true;
    }

5.绑定地址信息

// 绑定地址和端口
bool Bind(const std::string &ip, uint16_t port)
{
    // 创建一个 sockaddr_in 结构体并设置相关参数
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;  // 设置地址族为IPv4

    // 将端口号转换为网络字节顺序
    addr.sin_port = htons(port);

    // 将IP地址转换为网络字节顺序并填入结构体中
    addr.sin_addr.s_addr = inet_addr(ip.c_str());

    // 计算地址结构体的长度
    socklen_t len = sizeof(struct sockaddr_in);

    // 调用bind函数将套接字和地址绑定
    int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
    if (ret < 0)
    {
        // 绑定失败时输出错误日志信息
        ERR_LOG("BIND ADDRESS FAILED!");
        return false;
    }

    // 绑定成功
    return true;
}

6.开始监听连接请求

// 开始监听连接请求
bool Listen(int backlog = MAX_LISTEN)
{
    // 调用listen函数开始监听连接请求
    int ret = listen(_sockfd, backlog);
    if (ret < 0)
    {
        // 监听失败时输出错误日志信息
        ERR_LOG("SOCKET LISTEN FAILED!");
        return false;
    }

    // 监听成功
    return true;
}

7.向服务器发起连接

// 向服务器发起连接
bool Connection(const std::string &ip, uint16_t port)
{
    // 创建一个 sockaddr_in 结构体并设置相关参数
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;  // 设置地址族为IPv4

    // 将端口号转换为网络字节顺序
    addr.sin_port = htons(port);

    // 将IP地址转换为网络字节顺序并填入结构体中
    addr.sin_addr.s_addr = inet_addr(ip.c_str());

    // 计算地址结构体的长度
    socklen_t len = sizeof(struct sockaddr_in);

    // 调用connect函数发起连接请求
    int ret = connect(_sockfd, (struct sockaddr *)&addr, len);
    if (ret < 0)
    {
        // 连接失败时输出错误日志信息
        ERR_LOG("CONNECT SERVER FAILED!");
        return false;
    }

    // 连接成功
    return true;
}

8.获取新连接

// 获取新连接
int Accept()
{
    // 调用accept函数接受新的连接
    // int accept(int sockfd, struct sockaddr *addr, socklen_t *len);
    int newfd = accept(_sockfd, NULL, NULL);
    if (newfd < 0)
    {
        // 接受连接失败时输出错误日志信息
        ERR_LOG("SOCKET ACCEPT FAILED!");
        return -1;
    }

    // 返回新的文件描述符
    return newfd;
}

9.接收数据

// 接收数据
ssize_t Recv(void *buf, size_t len, int flag = 0)
{
    // 调用recv函数接收数据
    ssize_t s = recv(_sockfd, buf, len, flag);
    if (s <= 0)
    {
        if (errno == EINTR || errno == EAGAIN)
        {
            // 如果是由于被信号中断或者暂时没有数据可读造成的接收失败,则记录日志并返回0
            INF_LOG("Recv has not ready!");
            return 0;
        }
        // 其他接收失败的情况,记录错误日志信息并返回-1
        ERR_LOG("read failed!");
        return -1;
    }

    // 返回实际接收的数据长度
    return s;
}

10.非阻塞接收数据

// 非阻塞接收数据
ssize_t NonBlockRecv(void *buf, size_t len)
{
    // 调用Recv函数,设置MSG_DONTWAIT标志表示非阻塞接收
    return Recv(buf, len, MSG_DONTWAIT);
}

11.发送数据

// 发送数据
ssize_t Send(const void *buf, size_t len, int flag = 0)
{
    // 调用send函数发送数据
    // ssize_t send(int sockfd, void *data, size_t len, int flag);
    ssize_t ret = send(_sockfd, buf, len, flag);
    if (ret < 0)
    {
        // 发送失败时记录错误日志信息并返回-1
        ERR_LOG("SOCKET SEND FAILED!!");
        return -1;
    }

    // 返回实际发送的数据长度
    return ret;
}

12.非阻塞发送数据

// 非阻塞发送数据
ssize_t NonBlockSend(void *buf, size_t len)
{
    // 调用Send函数,设置MSG_DONTWAIT标志表示非阻塞发送
    return Send(buf, len, MSG_DONTWAIT);
}

13.关闭套接字

// 关闭套接字
void Close()
{
    // 检查套接字是否有效,如果有效则关闭套接字
    if (_sockfd != -1)
        close(_sockfd);
    
    // 将套接字文件描述符设置为无效值
    _sockfd = -1;
}

14.创建一个服务端连接

// 创建一个服务端连接
bool CreateServer(uint16_t port, const std::string &ip = DEFAULT_IP, bool block_flag = false)
{
    // 1. 创建套接字 
    // 2. 绑定地址 
    // 3. 开始监听 
    // 4. 设置非阻塞 
    // 5. 启动地址重用

    // 如果创建套接字失败,则返回false
    if (Create() == false) return false;

    // 如果需要设置为非阻塞模式,则调用NonBlock函数
    if (block_flag) NonBlock();

    // 绑定地址,如果绑定失败则返回false
    if (Bind(ip, port) == false) return false;

    // 开始监听,如果监听失败则返回false
    if (Listen() == false) return false;

    // 启动地址重用
    ReuseAddress();

    return true;
}

15.创建一个客户端连接

// 创建一个客户端连接
bool CreateClient(uint16_t port, const std::string &ip)
{
    // 1. 创建套接字 
    // 2. 指向连接服务器

    // 如果创建套接字失败,则返回false
    if (Create() == false)  
        return false;

    // 连接服务器,如果连接失败则返回false
    if (Connection(ip, port) == false)
        return false;

    return true;
}

16.设置套接字选项——开启地址端口重用

// 设置套接字选项——开启地址端口重用
void ReuseAddress()
{
    // 使用setsockopt函数设置SO_REUSEADDR和SO_REUSEPORT选项开启地址和端口重用
    int val = 1;
    setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));
    val = 1;
    setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));
}

17. 设置套接字阻塞属性——设置为非阻塞

// 设置套接字阻塞属性——设置为非阻塞
void NonBlock()
{
    // 使用fcntl函数获取当前套接字的属性,并设置为非阻塞模式
    //int fcntl(int fd,int cmd,.../* arg */);
    int flag = fcntl(_sockfd, F_GETFL, 0);
    fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
}

18.测试代码

这段代码是一个简单的基于Socket的服务器端程序,它创建一个服务器Socket并监听指定端口(8500)。然后在一个无限循环中接受客户端连接,接收客户端发送的数据,并将数据原样发送回客户端,最后关闭与客户端的连接。注意:需要打开两个终端分别运行服务器端和客户端

//服务器端
#include"../source/server.hpp"
int main(){
    Socket lst_sock;
    lst_sock.CreateServer(8500);
    while(1){
       int newfd =lst_sock.Accept();
       if(newfd <0){
           continue;
       }  
       Socket cli_sock(newfd);
       char buf[1024]={0};
       int ret= cli_sock.Recv(buf, 1023);
       if(ret < 0){
           cli_sock.Close();
           continue;
       }
       cli_sock.Send(buf, ret);
       cli_sock.Close();
    }
    lst_sock.Close();
    return 0;
}
//客户端口
#include"../source/server.hpp"
int main()
{
    Socket cli_sock;
    cli_sock.CreateClient(8500,"127.0.0.1");
    std::string str="hello have a good day~";
    cli_sock.Send(str.c_str(),str.size());
    char buf[1024]={0};
    cli_sock.Recv(buf,1023);
    DBG_LOG("%s",buf);
    return 0;
}

测试结果:
在这里插入图片描述

19.整体源代码

// Socket //
#define MAX_LISTEN 1024
#define DEFAULT_IP "0.0.0.0"
class Socket
{
private:
    int _sockfd;
public:
    Socket() : _sockfd(-1) {}
    Socket(int sockfd) : _sockfd(sockfd) {}
    ~Socket() { Close(); }
    // 获取套接字文件描述符
    int get_fd() { return _sockfd; }

    // 创建套接字
    bool Create()
    {
        // 调用socket函数创建套接字
        // int socket(int domain, int type, int protocol)
        _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        // 检查套接字创建是否成功
        if (_sockfd < 0)
        {
            // 套接字创建失败时输出日志信息
            INF_LOG("Socket creation failed");
            return false;
        }

        // 套接字创建成功
        return true;
    }

    // 绑定地址和端口
    bool Bind(const std::string &ip, uint16_t port)
    {
        // 创建一个 sockaddr_in 结构体并设置相关参数
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;  // 设置地址族为IPv4

        // 将端口号转换为网络字节顺序
        addr.sin_port = htons(port);

        // 将IP地址转换为网络字节顺序并填入结构体中
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        // 计算地址结构体的长度
        socklen_t len = sizeof(struct sockaddr_in);

        // 调用bind函数将套接字和地址绑定
        int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
        if (ret < 0)
        {
            // 绑定失败时输出错误日志信息
            ERR_LOG("BIND ADDRESS FAILED!");
            return false;
        }

        // 绑定成功
        return true;
    }

    // 开始监听连接请求
    bool Listen(int backlog = MAX_LISTEN)
    {
        // 调用listen函数开始监听连接请求
        int ret = listen(_sockfd, backlog);
        if (ret < 0)
        {
            // 监听失败时输出错误日志信息
            ERR_LOG("SOCKET LISTEN FAILED!");
            return false;
        }

        // 监听成功
        return true;
    }


    // 向服务器发起连接
    bool Connection(const std::string &ip, uint16_t port)
    {
        // 创建一个 sockaddr_in 结构体并设置相关参数
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;  // 设置地址族为IPv4

        // 将端口号转换为网络字节顺序
        addr.sin_port = htons(port);

        // 将IP地址转换为网络字节顺序并填入结构体中
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        // 计算地址结构体的长度
        socklen_t len = sizeof(struct sockaddr_in);

        // 调用connect函数发起连接请求
        int ret = connect(_sockfd, (struct sockaddr *)&addr, len);
        if (ret < 0)
        {
            // 连接失败时输出错误日志信息
            ERR_LOG("CONNECT SERVER FAILED!");
            return false;
        }

        // 连接成功
        return true;
    }

   
    // 获取新连接
    int Accept()
    {
        // 调用accept函数接受新的连接
        // int accept(int sockfd, struct sockaddr *addr, socklen_t *len);
        int newfd = accept(_sockfd, NULL, NULL);
        if (newfd < 0)
        {
            // 接受连接失败时输出错误日志信息
            ERR_LOG("SOCKET ACCEPT FAILED!");
            return -1;
        }

        // 返回新的文件描述符
        return newfd;
    }


    // 接收数据
    ssize_t Recv(void *buf, size_t len, int flag = 0)
    {
        // 调用recv函数接收数据
        ssize_t s = recv(_sockfd, buf, len, flag);
        if (s <= 0)
        {
            if (errno == EINTR || errno == EAGAIN)
            {
                // 如果是由于被信号中断或者暂时没有数据可读造成的接收失败,则记录日志并返回0
                INF_LOG("Recv has not ready!");
                return 0;
            }
            // 其他接收失败的情况,记录错误日志信息并返回-1
            ERR_LOG("read failed!");
            return -1;
        }

        // 返回实际接收的数据长度
        return s;
    }

   
    // 非阻塞接收数据
    ssize_t NonBlockRecv(void *buf, size_t len)
    {
        // 调用Recv函数,设置MSG_DONTWAIT标志表示非阻塞接收
        return Recv(buf, len, MSG_DONTWAIT);
    }


    // 发送数据
    ssize_t Send(const void *buf, size_t len, int flag = 0)
    {
        // 调用send函数发送数据
        // ssize_t send(int sockfd, void *data, size_t len, int flag);
        ssize_t ret = send(_sockfd, buf, len, flag);
        if (ret < 0)
        {
            // 发送失败时记录错误日志信息并返回-1
            ERR_LOG("SOCKET SEND FAILED!!");
            return -1;
        }

        // 返回实际发送的数据长度
        return ret;
    }

   
    // 非阻塞发送数据
    ssize_t NonBlockSend(void *buf, size_t len)
    {
        // 调用Send函数,设置MSG_DONTWAIT标志表示非阻塞发送
        return Send(buf, len, MSG_DONTWAIT);
    }


    // 关闭套接字
    void Close()
    {
        // 检查套接字是否有效,如果有效则关闭套接字
        if (_sockfd != -1)
            close(_sockfd);
        
        // 将套接字文件描述符设置为无效值
        _sockfd = -1;
    }

   
    // 创建一个服务端连接
    bool CreateServer(uint16_t port, const std::string &ip = DEFAULT_IP, bool block_flag = false)
    {
        // 1. 创建套接字 
        // 2. 绑定地址 
        // 3. 开始监听 
        // 4. 设置非阻塞 
        // 5. 启动地址重用

        // 如果创建套接字失败,则返回false
        if (Create() == false) return false;

        // 如果需要设置为非阻塞模式,则调用NonBlock函数
        if (block_flag) NonBlock();

        // 绑定地址,如果绑定失败则返回false
        if (Bind(ip, port) == false) return false;

        // 开始监听,如果监听失败则返回false
        if (Listen() == false) return false;

        // 启动地址重用
        ReuseAddress();

        return true;
    }

    // 创建一个客户端连接
    bool CreateClient(uint16_t port, const std::string &ip)
    {
        // 1. 创建套接字 
        // 2. 指向连接服务器

        // 如果创建套接字失败,则返回false
        if (Create() == false)  
            return false;

        // 连接服务器,如果连接失败则返回false
        if (Connection(ip, port) == false)
            return false;

        return true;
    }
   
    // 设置套接字选项——开启地址端口重用
    void ReuseAddress()
    {
        // 使用setsockopt函数设置SO_REUSEADDR和SO_REUSEPORT选项开启地址和端口重用
        int val = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));
        val = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));
    }

   
    // 设置套接字阻塞属性——设置为非阻塞
    void NonBlock()
    {
        // 使用fcntl函数获取当前套接字的属性,并设置为非阻塞模式
        //int fcntl(int fd,int cmd,.../* arg */);
        int flag = fcntl(_sockfd, F_GETFL, 0);
        fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

灯塔:HTML笔记

网页由哪些部分组成&#xff1f; *文字 图片 音频 视频 超链接 程序员写的代码是通过浏览器转换成网页的 五大浏览器有哪些&#xff1f; *IE浏览器 *火狐浏览器&#xff08;Firefox&#xff09; *谷歌浏览器&#xff08;Chrome&#xff09; *Safari浏览器 *欧朋浏览器&…

AI新工具(20240301) Ideogram; Image to Music Generator等

1: Ideogram 全新的多模态生图AI工具&#xff0c;以其优秀的文字渲染能力和生图能力受到业界瞩目 Ideogram是一个创新的AI工具&#xff0c;它通过在生成的图片中自然地整合文字&#xff0c;解决了生图AI领域长期存在的一个难题。这个工具特别擅长将文本以极其自然和协调的方式…

第三百七十五回

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"相关的内容&#xff0c;本章回中将介绍如何让Text组件中的文字自动换行.闲话休提&#xff0c;让我们一起Talk Flutter吧。 …

铝型材【欧标】

2020&#xff1a; 3030&#xff1a; 4040&#xff1a; 欧标T型螺丝 2020&#xff1a; 10最大 20120 59 3030&#xff1a; 12最大 30150 76 4040&#xff1a; 40最大 40200 …

RV1126芯片概述

RV1126芯片概述 前言1 主要特性2 详细参数 前言 1 主要特性 四核 ARM Cortex-A7 and RISC-V MCU250ms快速开机2.0Tops NPU14M ISP with 3帧 HDR支持3个摄像头同时输入4K H.264/H.265 视频编码和解码 2 详细参数

TikTok矩阵系统功能怎么写?常用源代码是什么?

TikTok矩阵系统的功能是如何编写的?又有哪些常用的源代码支撑这些功能呢?本文将通过五段源代码的分享&#xff0c;为大家揭开TikTok矩阵系统的神秘面纱。 一、TikTok矩阵系统的核心功能 TikTok的矩阵系统涵盖了多个核心功能&#xff0c;包括但不限于用户管理、内容分发、推…

MacBook将iPad和iPhone备份到移动硬盘

#创作灵感# 一个是ICloud不够用&#xff0c;想备份到本地&#xff1b;然而本地存储不够用&#xff0c;增加容量巨贵&#xff0c;舍不得这个钱&#xff0c;所以就想着能不能备份到移动硬盘。刚好有个移动固态&#xff0c;所以就试了一下&#xff0c;还真可以。 #正文# 说一下逻…

你真的了解C语言中的【柔性数组】吗~

柔性数组 1. 什么是柔性数组2. 柔性数组的特点3. 柔性数组的使用4. 柔性数组的优势 1. 什么是柔性数组 也许你从来没有听说过柔性数组这个概念&#xff0c;但是它确实是存在的。 C99中&#xff0c;结构体中的最后⼀个元素允许是未知大小的数组&#xff0c;这就叫做柔性数组成员…

DiskMirror-spring-boot-starter 技术|

DiskMirror-spring-boot-starter 技术 diskMirror 实现了 SpringBoot 的 starter 能够集成到 SpringBoot 中。 DiskMirror 的 starter&#xff0c;通过引入此类&#xff0c;可以直接实现 diskMirror 在 SpringBoot 中的自动配置&#xff0c;接下来我们将使用案例逐步的演示 d…

AI视频又又炸了!照片+声音变视频,阿里让Sora女主唱歌小李子说rap

Sora之后&#xff0c;居然还有新的AI视频模型&#xff0c;能惊艳得大家狂转狂赞&#xff01; 有了它&#xff0c;《狂飙》大反派高启强化身罗翔&#xff0c;都能给大伙儿普法啦&#xff08;狗头&#xff09;。 这就是阿里最新推出的基于音频驱动的肖像视频生成框架&#xff0c;…

马斯克正式起诉OpenAI和奥特曼!

就在刚刚&#xff0c;马斯克闹出来一件大事——正式起诉OpenAI和Sam Altman&#xff0c;并要求OpenAI 恢复开源GPT-4等模型&#xff01; 众所周知&#xff0c;马斯克这两年一只在推特上指责 OpenAI是CloseAI(不开源)&#xff0c;但都只是停留在口头上。 而这次马斯克动了真格。…

搭建LNMP环境并搭建论坛和博客

目录 一、LNMP架构原理 二、编译安装Nginx 三、编译安装MySQL 四、编译安装PHP 五、配置Nginx支持PHP解析 六、安装论坛 七、安装博客 一、LNMP架构原理 LNMP架构&#xff0c;是指在Linux平台下&#xff0c;由运行Nginx的web服务器&#xff0c;运行PHP的动态页面解析程序…

聚道云软件连接器2月新增应用/产品更新合集

2月更新概要 新增应用&#xff1a; 应用1&#xff1a;旺店通 应用2&#xff1a;明道云 应用3&#xff1a;春雨医生 应用4&#xff1a;姿美堂 应用5&#xff1a;三维家 新增&更新功能 1、【流程】中增加版本管理功能 新增应用 应用1&#xff1a;旺店通 旺店通ERP隶…

38.云原生之Istio安全-流量鉴权加密

云原生专栏大纲 文章目录 TLS 和 mTLSTLS 和 mTLS使用场景TLS 加密通信的流程终止 TLS什么时候用 mTLS&#xff1f;什么时候不用 mTLS&#xff1f; 流量加密入口流量加密内部流量加密PeerAuthentication 为工作负载设置 mTLSDestinationRule 为工作负载设置 mTLS 安全最佳实战…

(定时器/计数器)中断系统(详解与使用)

讲解 简介 定时器/计数器 定时器实际上也是计数器,只是计数的是固定周期的脉冲 定时和计数只是触发来源不同(时钟信号和外部脉冲)其他方面是一样的。 定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒”,计数单元的数值就增加一,当计数单元数值…

Qt应用软件【测试篇】vargrid内存检查工具

文章目录 vargrid介绍vargrid官网vargrid安装常用命令Valgrind的主要命令vargrid介绍 Valgrind是一个用于构建动态分析工具的框架,能自动检测许多内存管理和线程错误,并详细分析程序性能。Valgrind发行版包括七个成熟工具:内存错误检测器、两个线程错误检测器、缓存和分支预…

防御保护课程笔记

内容安全 防病毒 过滤技术 密码学

基于java ssm springboot+VUE疫情防疫系统系统前后端分离设计和实现

基于java ssm springbootVUE疫情防疫系统系统前后端分离设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐…

vue3使用百度地图实现个性化地图和轨迹

vue3使用百度地图实现个性化地图和轨迹 最终效果如图&#xff1a; 步骤如下&#xff1a; 一、百度地图在vue3中的引入 1.首先在百度地图开发中心中申请ak&#xff08;不多介绍&#xff09; 2.两种引入方式&#xff1a;在 index.html 中直接引入&#xff1b;使用npm导包。&…

【已亲测有效】如何彻底删除nodejs,避免影响安装新版本

第一步开始菜单搜索uninstall node.js&#xff0c;点击之后等待删除&#xff08;删除node_modules文件夹以及以下这些文件&#xff09; 第二步手动删除nodejs下载位置的其他文件夹。&#xff08;就是另外自己新建的两个文件夹node_cache和node_global&#xff09; 到这里其实应…