【Socket 编程 】基于UDP协议实现通信并添加简单业务

news2024/9/22 1:35:39

文章目录

  • 前言
  • 实现echo server
    • 对于服务器端
    • 对于客户端
    • UdpServer.hpp文件
    • nococpy.hpp文件
    • InetAddr.hpp头文件
    • Log.hpp头文件
    • UdpServerMain.cpp源文件
    • UdpClientMain.cpp源文件
    • 运行结果
  • 实现翻译业务
    • Dict.hpp头文件
    • UdpServerMain.cpp
    • Udpserver.hpp
    • 运行结果

前言

在了解了Socket编程的相关接口之后,我们来尝试通过代码实现简单场景来理解UDP协议通信

实现echo server

下面通过代码来简单的回显服务器和客户端之间的输入输出通信

对于服务器端

  1. 建立套接字(IPv4协议和UDP协议
  2. 绑定套接字
  3. 读取接收到的消息
  4. 发送响应
  5. 结束连接,关闭套接字

对于客户端

  1. 获取服务器端的IP和端口号
  2. 建立套接字
  3. 发送消息
  4. 读取服务器端发来的响应
  5. 结束连接,关闭套接字

为什么客户端不用bind绑定套接字呢,这是因为客户端在发送消息的时候,操作系统会自动地绑定本机IP和一个随机的端口号到sockfd,这是为了避免端口号冲突(指定一个端口号可能是其它进程占有的)

下面给出具体实现的代码:

UdpServer.hpp文件

该文件定义了一个服务器类,用来生成一个服务器对象,根据构造时传入的端口号来绑定套接字,且允许服务器在所有可用的网络接口上进行监听。这样,服务器可以接收发往任何本地IP地址的连接请求
代码如下:

#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include "LockGuard.hpp"
#include "Log.hpp"
#include "nocopy.hpp"
#include "InetAddr.hpp"
using namespace log_ns;

static const int gsockfd = -1;
static const uint16_t glocalport = 8888;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR
};

class UdpServer : public nocopy
{
private:
public:
    UdpServer(uint16_t localport = glocalport)
        : _localport(localport), _isrunning(false), _sockfd(gsockfd)
    {
    }

    void InitServer()//初始化服务端
    {
        // 创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // IPv4协议和UDP协议的套接字
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket create error!\n");
            exit(1);
        }
        LOG(DEBUG, "socket create success,sockfd is %d\n", _sockfd);
        // 绑定端口号和IP
        // 绑定之前创建一个sockaddr_in对象,存储本地地址信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY; // 服务器端,进行任意IP地址绑定
        local.sin_port = htons(_localport);

        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "sockfd bind error!\n");
            exit(1);
        }
        LOG(DEBUG, "sockfd bind success!\n");
    }

    void Start()//服务的开始工作
    {
        _isrunning = true;
        char inbuff[1024];
        while (_isrunning)
        {
            // 获取数据
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            ssize_t n = recvfrom(_sockfd, inbuff, sizeof(inbuff) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n < 0)
            {
                LOG(FATAL, "recvfrom error!\n");
            }
            else
            {
                LOG(DEBUG, "recvfrom success!\n");
                InetAddr addr(peer);
                inbuff[n] = '\0';
                cout << "[" << addr.Ip() << ":" << addr.Port() << "]# " << inbuff << endl;
                string echo_string = "[udp_server echo]# ";
                echo_string += inbuff;
                // 发送响应给客户端

                ssize_t res = sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr *)&peer, len);
                if (res < 0)
                {
                    LOG(FATAL, "echo sendto error!\n");
                }
                else
                {
                    LOG(DEBUG, "echo sendto success!\n");
                }
            }
        }
    }

    ~UdpServer()
    {
        if (_sockfd >= 0)
            close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _localport;
    bool _isrunning;
};

nococpy.hpp文件

该文件实现了一个不可拷贝构造和赋值拷贝的类,该类的所有派生类都不可以拷贝构造和赋值拷贝

#pragma once

// 设计一个禁止拷贝和赋值的类,其派生类也不允许拷贝和赋值
class nocopy
{
public:
    nocopy(){};
    ~nocopy(){};
    nocopy(const nocopy &) = delete;
    const nocopy &operator=(const nocopy &) = delete;
};

InetAddr.hpp头文件

该文件实现了一个可以将网络序列包括IP和端口号转化成主机序列的类

#pragma conce
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
using namespace std;
// 创建一个类用于将网络序列地址转换成主机序列地址
class InetAddr
{
private:
    void ToHest(const sockaddr_in &addr)
    {
        _port = ntohs(addr.sin_port);
        _ip = inet_ntoa(addr.sin_addr);
    }

public:
    InetAddr(const sockaddr_in &addr)
    {
        ToHest(addr);
    }

    string Ip()
    {
        return _ip;
    }

    uint16_t Port()
    {
        return _port;
    }

private:
    string _ip;
    uint16_t _port;
    struct sockaddr_in _addr;
};

Log.hpp头文件

该文件定义了一个日志类,并提供了宏来进行便捷的打印日志

#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
#include <fstream>
#include <sys/types.h>
#include <cstdarg>

#include <ctime>
#include "LockGuard.hpp"

namespace log_ns
{
    enum // 日志等级
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string LevelToString(int level) // 转换日志等级为字符串
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }

    struct logmessage
    {
    public:
        std::string _level;
        pid_t id;
        std::string _filename;
        int _filenumber;
        std::string _curr_time;
        std::string _message_info;
    };

#define SCREEN_TYPE 1
#define FILE_TYPE 2

    const std::string glogfile = "./log.txt";
    pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;

    class Log
    {
    private:
        std::string CurrTimeToString()
        {
            time_t now = time(nullptr);
            struct tm *curr_time = localtime(&now);
            char buff[128];
            snprintf(buff, sizeof(buff), "%d-%02d-%02d %02d:%02d:%02d", curr_time->tm_year - 1900, curr_time->tm_mon + 1, curr_time->tm_mday, curr_time->tm_hour, curr_time->tm_min, curr_time->tm_sec);
            return buff;
        }

        void FlushLogToScreen(const logmessage &lg) // 将日志信息输出到显示器文件中
        {
            printf("[%s][%d][%s][%d][%s]:%s \n", lg._level.c_str(), lg.id, lg._filename.c_str(), lg._filenumber, lg._curr_time.c_str(), lg._message_info.c_str());
        }

        void FlushLogToFile(const logmessage &lg) // 将日志信息输出到指定文件中
        {
            std::ofstream out(_logfile, std::ios::app);
            if (!out.is_open())
            {
                return;
            }
            char buff[2048] = {'\0'};
            snprintf(buff, sizeof(buff), "[%s][%d][%s][%d][%s]:%s \n", lg._level.c_str(), lg.id, lg._filename.c_str(), lg._filenumber, lg._curr_time.c_str(), lg._message_info.c_str());
            out.write(buff, sizeof(buff));

            out.close();
        }

    public:
        Log(const std::string &file = glogfile)
            : _logfile(file), _type(SCREEN_TYPE)
        {
        }
        // 打印日志
        void PrintLog(const logmessage &lg)
        {
            LockGuard lockguard(&glock);
            switch (_type)
            {
            case SCREEN_TYPE:
                FlushLogToScreen(lg);
                break;
            case FILE_TYPE:
                FlushLogToFile(lg);
                break;
            }
        }
        // 切换输出方式
        void Enable(int type)
        {
            _type = type;
        }

        // 构造logmessage对象,并打印
        void LogMessage(std::string filename, int filenumber, int level, const char *format, ...) //...表示可变参数列表
        {
            logmessage lg;

            lg._filename = filename;
            lg._filenumber = filenumber;
            lg._level = LevelToString(level);
            // std::cout << lg._level << std::endl;
            lg.id = getpid();
            lg._curr_time = CurrTimeToString();
            // lg._message_info = CurrTimeToString();
            va_list ap;           // 获取所有参数
            va_start(ap, format); // 取出参数包的部分
            char log_info[1024];
            vsnprintf(log_info, sizeof(log_info), format, ap); // 格式转换
            va_end(ap);
            lg._message_info = log_info;
            // 打印这个日志
            PrintLog(lg);
        }

    private:
        int _type;
        std::string _logfile;
    };

    Log lg;
#define LOG(LeveL, Format, ...)                                          \
    do                                                                   \
    {                                                                    \
        lg.LogMessage(__FILE__, __LINE__, LeveL, Format, ##__VA_ARGS__); \
    } while (0)

#define EnableScreen()          \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)

#define EnableFile()          \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
};

UdpServerMain.cpp源文件

实现了服务器端的处理逻辑

#include "UdpServer.hpp"
#include <iostream>
#include <memory>
using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cerr << "Usage:" << argv[0] << " local-port" << endl;
        exit(0);
    }

    uint16_t port = stoi(argv[1]);
    EnableScreen();
    unique_ptr<UdpServer> usvr = make_unique<UdpServer>(port);
    usvr->InitServer();
    usvr->Start();
    return 0;
}

UdpClientMain.cpp源文件

简单模拟了一个客户端,包括处理数据的逻辑

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include <memory>
using namespace std;
int main(int argc, char *argv[])
{

    if (argc != 3)
    {
        cerr << "Usage:" << argv[0] << " local-port" << endl;
        exit(0);
    }
    string ip = argv[1];
    uint16_t port = stoi(argv[2]);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        cerr << "client create sockfd error!\n"
             << endl;
    }
    // 客户端一般不用绑定套接字,操作系统会在第一次发送消息时自动绑定本机ip和一个随机的port
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    server.sin_port = htons(port);
    // 执行向服务端发送数据以及接收服务端响应
    while (true)
    {
        string line;
        cout << "please input: ";
        getline(cin, line);
        ssize_t res = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&server, sizeof(server));

        if (res > 0)
        {
            struct sockaddr_in temp;
            memset(&temp, 0, sizeof(temp));
            char buff[1024];
            socklen_t len = 0;
            int n = recvfrom(sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&temp, &len);
            if (n > 0)
            {
                buff[n] = '\0';
                cout << buff << endl;
            }
            else
            {
                cerr << "client recvfrom error" << endl;
                break;
            }
        }
        else
        {
            cout << "client sendto error" << endl;
            break;
        }
    }
    return 0;
}

运行结果

在这里插入图片描述
查看网络连接的信息
在这里插入图片描述

实现翻译业务

根据上面的代码我们已经能够基本实现客户端与服务端之间的简单交流,现在可以考虑给服务端添加业务功能,比如一个翻译功能。
于是呢我们的服务端的处理逻辑就变成了:

  • 创建套接字
  • 绑定套接字
  • 接收客户端要查的单词
  • 发送客户端翻译结果
  • 结束连接,关闭套接字

具体的,翻译功能实现如下

Dict.hpp头文件

该文件实现了一个可加载指定路径字典的类,并支持翻译功能。底层数据结构是unordered_map

#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include <unistd.h>
#include "Log.hpp"

using namespace log_ns;
using namespace std;

const static string gpath = "./Dict.txt";
const string sep = ": "; // 每行数据的分隔符

class Dict
{
private:
    void LoadDict(const string &path)
    {
        ifstream in(path); // 读取文件的文件流
        if (!in.is_open())
        {
            LOG(FATAL, "open %s failed\n", path.c_str());
            exit(1);
        }

        string line;
        while (getline(in, line))
        {
            LOG(DEBUG, "load info:%s,success\n", line.c_str());
            if (line.size() == 0)
            {
                continue;
            }
            int pos = line.find(sep);
            if (pos == string::npos)
            {
                continue;
            }
            string key = line.substr(0, pos);
            if (key.size() == 0)
                continue;
            string value = line.substr(pos + sep.size());
            if (value.size() == 0)
                continue;
            _dict[key] = value;
        }
        LOG(INFO, "load %s success!\n", path.c_str());
        in.close();
    }

public:
    Dict(const string &path = gpath)
        : _dict_path(path)
    {
        LoadDict(path); // 加载指定文件的字典数据
    }

    string Translate(string key)
    {
        if (!_dict.count(key))
            return "None";
        return _dict[key];
    }

    ~Dict()
    {
    }

private:
    unordered_map<string, string> _dict;
    string _dict_path;
};

对原来的服务端代码做出下面更改:

UdpServerMain.cpp

#include "UdpServer.hpp"
#include <iostream>
#include <memory>
#include "Dict.hpp"
using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cerr << "Usage:" << argv[0] << " local-port" << endl;
        exit(0);
    }

    uint16_t port = stoi(argv[1]);
    EnableScreen();

    Dict dic;//定义一个字典类
    func_t translate = std::bind(&Dict::Translate, &dic, std::placeholders::_1);
    unique_ptr<UdpServer> usvr = make_unique<UdpServer>(translate, port);
    usvr->InitServer();
    usvr->Start();
    return 0;
}

Udpserver.hpp

#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include "LockGuard.hpp"
#include "Log.hpp"
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include <functional>
using namespace log_ns;

static const int gsockfd = -1;
static const uint16_t glocalport = 8888;
using func_t = function<string(string)>;
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR
};

class UdpServer : public nocopy
{
private:
public:
    UdpServer(func_t func, uint16_t localport = glocalport)
        : _localport(localport), _isrunning(false), _sockfd(gsockfd), _func(func)
    {
    }

    void InitServer()
    {
        // 创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // IPv4协议和UDP协议的套接字
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket create error!\n");
            exit(1);
        }
        LOG(DEBUG, "socket create success,sockfd is %d\n", _sockfd);
        // 绑定端口号和IP
        // 绑定之前创建一个sockaddr_in对象,存储本地地址信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY; // 服务器端,进行任意IP地址绑定
        local.sin_port = htons(_localport);

        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "sockfd bind error!\n");
            exit(1);
        }
        LOG(DEBUG, "sockfd bind success!\n");
    }

    void Start()
    {
        _isrunning = true;
        char inbuff[1024];
        while (_isrunning)
        {
            // 获取数据
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            ssize_t n = recvfrom(_sockfd, inbuff, sizeof(inbuff) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n < 0)
            {
                LOG(FATAL, "recvfrom error!\n");
            }
            else
            {
                LOG(DEBUG, "recvfrom success!\n");
                InetAddr addr(peer);
                inbuff[n] = '\0';
                cout << "[" << addr.Ip() << ":" << addr.Port() << "]# " << inbuff << endl;
                string echo_string = "[udp_server echo]# ";
                string result = _func(inbuff);
                echo_string += result;
                // 发送响应给客户端
                ssize_t res = sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr *)&peer, len);
                if (res < 0)
                {
                    LOG(FATAL, "echo sendto error!\n");
                }
                else
                {
                    LOG(DEBUG, "echo sendto success!\n");
                }
            }
        }
    }

    ~UdpServer()
    {
        if (_sockfd >= 0)
            close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _localport;
    bool _isrunning;
    func_t _func;
};

运行结果

在这里插入图片描述

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

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

相关文章

最优化理论与方法-第十一讲-线性规划-极点的刻画

文章目录 1. 概述2. 线性规划定义3. 多面体的基本性质3.1 定义3.2 证明13.3 证明2 B站老师学习视频 1. 概述 线性规划的标准形式&#xff1b;多面体的几何分解&#xff1b;单纯形法&#xff1b;对偶单纯形法 2. 线性规划定义 线性规划Linear Programming&#xff1a;目标函数…

Ubuntu16.04环境下Baxter机器人开发环境搭建要点说明

Ubuntu16.04环境下Baxter机器人开发环境搭建要点说明 前面写过一篇文章&#xff0c;描述了在ubuntu20.04环境下baxter机器人开发环境的搭建&#xff0c;本人在后来的使用中&#xff0c;出于一些原因又在ubuntu16环境下搭建了开发环境&#xff0c;二者总体流程基本类似&#xf…

【数据分享】2013-2022年我国省市县三级的逐年SO2数据(excel\shp格式\免费获取)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000——2022年的省市县三级的逐年PM2.5数据和2013-2022年的省市县三级的逐年CO数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次我们分享的是我国2013——2022年的省…

【Spark官方文档部分翻译】RDD编程指南(RDD Programming Guide)

写在前面 内容如何选择 本翻译只翻译本人认为精华的部分&#xff0c;本人认为的Spark的一些核心理念&#xff0c;编程思想。一些特别基础的操作包括但不限于搭建环境就不在此赘述了。 配套版本 本系列基于Spark 3.3.1&#xff0c;Scala 2.12.10&#xff0c;进行翻译总结 原…

pycharm报错:No module named pip/No module named pytest

1、问题概述? 今天在执行一个python脚本的时候,控制台提示:No module named pytest,就是没有pytest模块,于是我使用pip命令进行安装,命令如下; pip install pytest 结果又提示No module named pip,说我没有pip模块,没办法,再安装pip 2、安装pip-方式1 在pycharm的T…

路由表与IP数据报转发:基础小白指南

目录 1. 路由表的基本概念 2. 路由表中的默认路由 3. IP数据报的转发流程 4. 路由聚合 5. 最长前缀匹配 总结 在网络世界中&#xff0c;IP数据报的转发是如何进行的&#xff1f; 这篇文章将带你深入了解路由表的基本概念和IP数据报的转发流程。我们会用简洁明了的语言和实…

华为mate20 激活设备

升级失败 需要 准备2个usb线 一根usb1.0工程线 一根正常线 。工程线怎么做&#xff0c;找一个10k电阻 把红线和绿线连起来。我用的是贴片电阻103 大概是2512 1W&#xff0c;也可以用金属膜电阻 步骤① 热风枪吹开后盖

vscode下运行django项目(命令与可视化操作两种方式python .\demo\manage.py runserver)

文章目录 实验前提1. 命令方式操作2. code图形方式操作 实验前提 vscode配置django环境并创建django项目&#xff08;全图文操作&#xff09; https://blog.csdn.net/xzzteach/article/details/140591167 1. 命令方式操作 python .\demo\manage.py runserver关闭运行 2. c…

App Instance 架构示例

前言 在Unity程序设计过程中&#xff0c;我们处理的第一个对象是Application Instance。 它的主要职责是启动流程管理、卸载流程管理&#xff0c;次要职责是管理在内部的子系统生命周期。其他职责&#xff0c;提供或桥接应用程序的配置信息、及其他第三方接口。 它通常以单例的…

buu做题(7)

[BJDCTF2020]Mark loves cat 开始的界面没啥东西, 看了下源码好像也没啥东西 用dirsearch扫描一下 有git 泄露 用工具githack下载源码 <?phpinclude flag.php;$yds "dog"; $is "cat"; $handsome yds;foreach($_POST as $x > $y){$$x $y; }f…

【过题记录】 7.21

Mad MAD Sum 算法&#xff1a;思维&#xff0c;前缀最大值 模拟一下他的运行过程就会发现&#xff0c;两次之后整个数组就固定了&#xff0c;之后每次都是每个数往后移动一位&#xff0c;可以模拟两次之后计算每个数的存活轮数&#xff0c;计算贡献。 #include<bits/stdc.h…

FreeRTOS 任务的挂起与恢复

任务的挂起与恢复的API函数 挂起 - vTaskSuspend 挂起任务类似暂停&#xff0c;可恢复&#xff1b; 删除任务&#xff0c;无法恢复&#xff0c;类似“人死两清” 使用 必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数 void vTaskSuspend( TaskHandle_t xTaskToSuspen…

GIT命令学习 二

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

PostgreSQL简介和安装

一、Postgresql简介&#xff1a; 1、PostgreSql是功能强大的&#xff0c;开源的关系型数据库&#xff0c;底层基于C语言实现&#xff1b; 2、开源&#xff1a;允许对PostgreSql进行封装&#xff0c;用于商业收费&#xff1b; 3、版本迭代速度快&#xff0c;正式版本已经到15.R…

《昇思25天学习打卡营第24天|基于 MindSpore 实现 BERT 对话情绪识别》

1. BERT 模型概述 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一个预训练的语言表示模型&#xff0c;可以应用于多种自然语言处理任务&#xff0c;包括对话情绪识别。BERT 通过双向 Transformer 编码器&#xff0c;能同时考虑句子…

【Langchain大语言模型开发教程】基于文档问答

&#x1f517; LangChain for LLM Application Development - DeepLearning.AI Embedding&#xff1a; https://huggingface.co/BAAI/bge-large-en-v1.5/tree/main 学习目标 1、Embedding and Vector Store 2、RetrievalQA 引包、加载环境变量 import osfrom dotenv import…

单例模式_Golang

目录 一、单例模式 1.1 基本概念 1.2 使用场景 二、Golang实现 2.1 懒汉模式&#xff08;Lazy Loading&#xff09; 一、单例模式 1.1 基本概念 一个类只能生成一个实例&#xff0c;且该类能自行创建这个实例的一种模式,这个定义个人感觉可以拆的通俗一些,在项目的生命周…

2-41 基于matlab的小车倒立摆系统的控制及GUI动画演示

基于matlab的小车倒立摆系统的控制及GUI动画演示。输入小车及倒立摆的初始参数&#xff0c;位置参数&#xff0c;对仿真时间和步长进行设置&#xff0c;通过LQR计算K值&#xff0c;进行角度、角速度、位置、速度仿真及曲线输出&#xff0c;程序已调通&#xff0c;可直接运行。 …

【BUG】已解决:ModuleNotFoundError: No module named‘ pip‘

已解决&#xff1a;ModuleNotFoundError: No module named‘ pip‘ 目录 已解决&#xff1a;ModuleNotFoundError: No module named‘ pip‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰…

JAVA零基础学习2(算术逻辑三元运算符、原码反码补码、标准的类如何描写)

JAVA零基础学习2&#xff08;算术逻辑三元运算符、原码反码补码、标准的类如何描写&#xff09; 算术运算符算术运算符自增和自减运算符算术运算符的优先级示例代码 逻辑运算符三元运算符示例代码示例1&#xff1a;简单的条件判断示例2&#xff1a;嵌套的三元运算符 原码反码补…