Linux---自定义协议

应用层协议

一、协议定制---以网络计算器为例

网络计算机功能---进行+-*/^&|的运算并返回结果

请求和响应的结构体如下

// Protocol.hpp
#pragma once
#include <iostream>
#include <memory>
class Request
{
public:
    Request()
    {
    }
    Request(int data_x, int data_y, char op)
        : _data_x(data_x), _data_y(data_y), _op(op)
    {
    }

private:
    int _data_x;
    int _data_y;
    char _op;
};
class Response
{
public:
    Response()
    {
    }
    Response(int result, int code)
        : _result(result), _code(code)
    {
    }

private:
    int _result;
    int _code;
};

// 建造类 --- 设计模式 --- 用来创建类对象
class Factory
{
public:
    std::shared_ptr<Request> BuiltRequest()
    {
        std::shared_ptr<Request> req = std::make_shared<Request>();
        return req;
    }
    std::shared_ptr<Request> BuiltRequest(int data_x, int data_y, char op)
    {
        std::shared_ptr<Request> req = std::make_shared<Request>(data_x, data_y, op);
        return req;
    }

    std::shared_ptr<Response> BuiltResponse()
    {
        std::shared_ptr<Response> resp = std::make_shared<Response>();
        return resp;
    }
    std::shared_ptr<Response> BuiltResponse(int result, int code)
    {
        std::shared_ptr<Response> resp = std::make_shared<Response>(result, code);
        return resp;
    }
};

将请求的数据按照怎样的方式序列化成为字符串呢?

我们选择将数据转换成 "len\nx op y\n"

  • len是"x op y"的字符串长度,"len"后面的\n用来将len和后面的有效数据分开
  • ”x op y“是要计算的表达式,比如”5 + 6“,中间加空格间隔开,它后面的\n是为了调试打印,可以不加

这里我们也可以直接选择将数据转化成 "x op y\n",用\n作为数据之间的间隔,但是如果我们要传递的数据中就包含\n,这样做就会出现问题,但是加"len\n"就不会,因为我们能保证len这个字符串中不可能出现\n,更具有通用性

二、序列化和反序列化

所以,序列化和反序列化本质就是字符串相关的操作,具体的代码如下

//新增 给字符串数据,加报头和去报头的函数
const std::string BlankSep = " ";
const std::string LineSep = "\n";

// 将"有效数据" ->  "len\n有效数据\n"
std::string Encode(const std::string &message)
{
    return std::to_string(message.size()) + LineSep + message + LineSep;
}

// 将"len\n有效数据\n" ->  "有效数据",这里要注意判断数据是否完整,即是否有一个完整的请求
bool Decode(std::string &package, std::string *message)
{
    auto pos = package.find(LineSep);
    if (pos == std::string::npos)
        return false;
    int len = std::stoi(package.substr(0, pos));
    int total = len + pos + 2 * LineSep.size();
    if (total > package.size())
        return false;
    *message = package.substr(pos + LineSep.size(), len);
    package.erase(0, total);
    return true;
}

class Request
{
    //...
public:
    // 新增 --- 序列化和反序列化两个函数
    // 将数据序列化为 "x op y", 其中BlankSep是" "
    void Serialize(std::string *out)
    {
        *out = std::to_string(_data_x) + BlankSep + _op + BlankSep + std::to_string(_data_y);
    }
    // 将"x op y"反序列化结构化字段, 其中BlankSep是" "
    bool Deserialize(const std::string &in)
    {
        auto l = in.find(BlankSep);
        if (l == std::string::npos)
            return false;
        _data_x = std::stoi(in.substr(0, l));
        auto r = in.rfind(BlankSep);
        if (r == std::string::npos)
            return false;
        _data_y = std::stoi(in.substr(r + BlankSep.size()));
        if (r - l - (int)BlankSep.size() != 1)
            return false;
        _op = in[l + BlankSep.size()];
        return true;
    }
};

class Response
{
    //...
public:
    // 新增 --- 序列化和反序列化两个函数
    // 将数据转序列化为"result code", 其中BlankSep是" "
    void Serialize(std::string *out)
    {
        *out = std::to_string(_result) + BlankSep + std::to_string(_code);
    }
    // 将"result code"反序列化为结构化字段, 其中BlankSep是" "
    bool Deserialize(const std::string &in)
    {
        auto pos = in.find(BlankSep);
        if (pos == std::string::npos)
            return false;
        _result = std::stoi(in.substr(0, pos));
        _code = std::stoi(in.substr(pos + BlankSep.size()));
        return true;
    }
};

当然这些字符串相关的序列化和反序列化操作过于繁琐,我们可以不用手写,可以直接用一些工具帮助我们完成这一过程,如json,protobuf等。

用json举个例子(注意要下载json相关的第三方库)

class Request
{
    //...
public:
    void Serialize(std::string *out)
    {
        Json::Value root;
        root["data_x"] = _data_x;
        root["data_y"] = _data_y;
        root["op"] = _op;
        Json::FastWriter writer;
        *out = writer.write(root);
    }

    bool Deserialize(const std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if (res)
        {
            _data_x = root["data_x"].asInt();
            _data_y = root["data_y"].asInt();
            _op = root["op"].asInt();
        }
        return res;
    }
};

class Response
{
    //...
public:
    void Serialize(std::string *out)
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;
        Json::FastWriter writer;
        *out = writer.write(root);
    }

    bool Deserialize(const std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if (res)
        {
            _result = root["result"].asInt();
            _code = root["code"].asInt();
        }
        return res;
    }
};

 Json序列化后的表达式如下

三、完整代码(包含对socket的封装)

// Calculate.hpp
#pragma once
#include <iostream>
#include "Protocol.hpp"
namespace Calcu
{
    enum
    {
        Success,
        DivZero,
        ModZero,
        Unknown
    };
    class Calculate
    {
    public:
        std::shared_ptr<Protocol::Response> cal(std::shared_ptr<Protocol::Request> req)
        {
            auto resp = factory.BuiltResponse();
            switch (req->GetOp())
            {
            case '+':
                resp->SetResult(req->GetX() + req->GetY());
                break;
            case '-':
                resp->SetResult(req->GetX() - req->GetY());
                break;
            case '*':
                resp->SetResult(req->GetX() * req->GetY());
                break;
            case '/':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(DivZero);
                }
                else
                {
                    resp->SetResult(req->GetX() / req->GetY());
                }
            }
            break;
            case '%':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(ModZero);
                }
                else
                {
                    resp->SetResult(req->GetX() % req->GetY());
                }
            }
            break;
            case '^':
                resp->SetResult(req->GetX() ^ req->GetY());
                break;
            case '|':
                resp->SetResult(req->GetX() | req->GetY());
                break;
            case '&':
                resp->SetResult(req->GetX() & req->GetY());
                break;
            default:
                resp->SetCode(Unknown);
                break;
            }
            return resp;
        }

    private:
        Protocol::Factory factory;
    };
}

//Protocol.hpp
#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <jsoncpp/json/json.h>

// #define SelfDefine 1

namespace Protocol
{
    const std::string BlankSep = " ";
    const std::string LineSep = "\n";
    std::string Encode(const std::string &message)
    {
        return std::to_string(message.size()) + LineSep + message + LineSep;
    }

    bool Decode(std::string &package, std::string *message)
    {
        auto pos = package.find(LineSep);
        if (pos == std::string::npos)
            return false;
        int len = std::stoi(package.substr(0, pos));
        int total = len + pos + 2 * LineSep.size();
        if (total > package.size())
            return false;
        *message = package.substr(pos + LineSep.size(), len);
        package.erase(0, total);
        return true;
    }

    class Request
    {
    public:
        Request() : _data_x(0), _data_y(0), _op(0)
        {
        }

        Request(int data_x, int data_y, char op)
            : _data_x(data_x), _data_y(data_y), _op(op)
        {
        }

        void Serialize(std::string *out)
        {
#ifdef SelfDefine
            *out = std::to_string(_data_x) + BlankSep + _op + BlankSep + std::to_string(_data_y);
#else
            Json::Value root;
            root["data_x"] = _data_x;
            root["data_y"] = _data_y;
            root["op"] = _op;
            Json::FastWriter writer;
            *out = writer.write(root);
#endif
        }

        bool Deserialize(const std::string &in)
        {
#ifdef SelfDefine
            auto l = in.find(BlankSep);
            if (l == std::string::npos)
                return false;
            _data_x = std::stoi(in.substr(0, l));
            auto r = in.rfind(BlankSep);
            if (r == std::string::npos)
                return false;
            _data_y = std::stoi(in.substr(r + BlankSep.size()));
            if (r - l - (int)BlankSep.size() != 1)
                return false;
            _op = in[l + BlankSep.size()];
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if (res)
            {
                _data_x = root["data_x"].asInt();
                _data_y = root["data_y"].asInt();
                _op = root["op"].asInt();
            }
            return res;
#endif
        }

        void Inc()
        {
            _data_x++;
            _data_y++;
        }

        void DeBug()
        {
            std::cout << "data_x :" << _data_x << std::endl;
            std::cout << "data_y :" << _data_y << std::endl;
            std::cout << "op :" << _op << std::endl;
        }

        int GetX() { return _data_x; }
        int GetY() { return _data_y; }
        char GetOp() { return _op; }

    private:
        // 序列化
        // len\nx op y\n
        int _data_x;
        int _data_y;
        char _op;
    };

    class Response
    {
    public:
        Response() : _result(0), _code(0)
        {
        }

        Response(int result, int code)
            : _result(result), _code(code)
        {
        }

        void Serialize(std::string *out)
        {
#ifdef SelfDefine
            *out = std::to_string(_result) + BlankSep + std::to_string(_code);
#else
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;
            Json::FastWriter writer;
            *out = writer.write(root);
#endif
        }

        bool Deserialize(const std::string &in)
        {
#ifdef SelfDefine
            auto pos = in.find(BlankSep);
            if (pos == std::string::npos)
                return false;
            _result = std::stoi(in.substr(0, pos));
            _code = std::stoi(in.substr(pos + BlankSep.size()));
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if (res)
            {
                _result = root["result"].asInt();
                _code = root["code"].asInt();
            }
            return res;
#endif
        }

        void SetResult(int res)
        {
            _result = res;
        }

        void SetCode(int code)
        {
            _code = code;
        }

        int GetResult()
        {
            return _result;
        }

        int GetCode()
        {
            return _code;
        }

    private:
        int _result;
        int _code;
    };

    // 建造类 --- 设计模式
    class Factory
    {
    public:
        std::shared_ptr<Request> BuiltRequest()
        {
            std::shared_ptr<Request> req = std::make_shared<Request>();
            return req;
        }
        std::shared_ptr<Request> BuiltRequest(int data_x, int data_y, char op)
        {
            std::shared_ptr<Request> req = std::make_shared<Request>(data_x, data_y, op);
            return req;
        }

        std::shared_ptr<Response> BuiltResponse()
        {
            std::shared_ptr<Response> resp = std::make_shared<Response>();
            return resp;
        }
        std::shared_ptr<Response> BuiltResponse(int result, int code)
        {
            std::shared_ptr<Response> resp = std::make_shared<Response>(result, code);
            return resp;
        }
    };
}

// 对sockfd进行封装
//对网络套接字进行封装
// Socket.hpp
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <string.h>
#include <memory>
#include <string>
#include <unistd.h>

const int defaultsockfd = -1;
const int defaultbacklog = 5;
#define CONV(addr) ((struct sockaddr *)addr)
namespace zxws
{
    enum
    {
        SockError = 1,
        BindError,
        ListenError,
        AcceptError,
        ConnectError
    };

    // 模板方法 --- 设计模式的一种
    // 对网络套接字进行封装 --- 屏蔽内部建立连接的过程
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void CreateSocket() = 0;
        virtual void BindSocket(uint16_t port) = 0;
        virtual void ListenSocketOrDie(int backlog) = 0;
        virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;
        virtual bool ConnectServer(const std::string &peerip, uint16_t peerport) = 0;
        virtual int GetSockfd() = 0;
        virtual void SetSockFd(int sockfd) = 0;
        virtual void CloseSockfd() = 0;

    public:
        void BuildListenSocket(uint16_t port, int backlog)
        {
            CreateSocket();
            BindSocket(port);
            ListenSocketOrDie(backlog);
        }

        bool BuildConnectSocket(const std::string &peerip, uint16_t peerport)
        {
            CreateSocket();
            return ConnectServer(peerip, peerport);
        }

        void BuildNormalSocket(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd)
        {
        }

        virtual ~TcpSocket() {}

        virtual void CreateSocket() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
                exit(SockError);
            std::cout << "create success , sockfd : " << _sockfd << std::endl;
        }

        virtual void BindSocket(uint16_t port) override
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(port);
            local.sin_addr.s_addr = INADDR_ANY;
            int n = bind(_sockfd, CONV(&local), sizeof(local));
            if (n < 0)
                exit(BindError);
            std::cout << "bing success , sockfd: " << _sockfd << std::endl;
        }

        virtual void ListenSocketOrDie(int backlog) override
        {
            int n = listen(_sockfd, backlog);
            if (n < 0)
                exit(ListenError);
            std::cout << "listen success , sockfd: " << _sockfd << std::endl;
        }

        virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport)
        {
            struct sockaddr_in server;
            socklen_t len = sizeof(server);
            int sockfd = accept(_sockfd, CONV(&server), &len);
            if (sockfd < 0)
                return nullptr;
            char buffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &server.sin_addr, buffer, sizeof(buffer));
            *peerip = buffer;
            *peerport = ntohs(server.sin_port);
            return new TcpSocket(sockfd);
        }

        virtual bool ConnectServer(const std::string &peerip, uint16_t peerport) override
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(peerport);
            inet_pton(_sockfd, peerip.c_str(), &server.sin_addr);
            int n = connect(_sockfd, CONV(&server), sizeof(server));
            return n == 0;
        }

        virtual int GetSockfd()
        {
            return _sockfd;
        }

        virtual void SetSockFd(int sockfd)
        {
            _sockfd = sockfd;
        }

        void CloseSockfd()
        {
            if (_sockfd >= 0)
                close(_sockfd);
        }

    private:
        int _sockfd;
    };
}


//TcpServer.hpp
#pragma once
#include "Socket.hpp"
#include <pthread.h>
#include <functional>

using func_t = std::function<std::string(std::string&,bool*)>;
class TcpServer;

class ThreadData
{
public:
    ThreadData(TcpServer* tcp_this,zxws::Socket* sockp)
        :_this(tcp_this),_sockp(sockp)
    {}
public:
    TcpServer*_this;
    zxws::Socket* _sockp;
};

class TcpServer
{
public:
    TcpServer(int port, func_t task)
        : _port(port), _listensockfd(new zxws::TcpSocket()), _task(task)
    {
    }

    static void *ThreadRoutine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        std::string inbuffer;
        while (1)
        {
            bool ok = true;
            // 1、接收
            if(!td->_sockp->Recv(&inbuffer,1024))
                break;
            // 2、处理
            std::string send_str = td->_this->_task(inbuffer,&ok);
            if(ok)
            {
                if(!send_str.empty())
                {
                    // 3、发送
                    td->_sockp->Send(send_str);
                }
            }
            else
            {
                break;
            }
        }
        //注意顺序
        td->_sockp->CloseSockfd();
        delete td->_sockp;
        delete td;
        return nullptr;
    }

    void Loop()
    {
        _listensockfd->BuildListenSocket(_port, defaultbacklog);
        std::string ip;
        uint16_t port;
        while (true)
        {
            zxws::Socket *sockfd = _listensockfd->AcceptConnection(&ip, &port);
            if (sockfd == nullptr)
                continue;
            std::cout << "accept success , [" << ip << ":" << port << "]"
                      << " sockfd " << sockfd->GetSockfd() << std::endl;
            // sockfd->CloseSockfd();
            ThreadData *td = new ThreadData(this,sockfd);
            pthread_t tid;
            pthread_create(&tid, nullptr, ThreadRoutine, td);
        }
    }

    ~TcpServer()
    {
        delete _listensockfd;
    }

private:
    int _port;
    zxws::Socket *_listensockfd;
    func_t _task;
};

//TcpServer.cpp
#include "Socket.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Calculate.hpp"
using namespace Protocol;
using namespace zxws;
using namespace Calcu;
std::string Handler(std::string &inbuffer, bool *error_code)
{
    *error_code = true;
    std::unique_ptr<Factory> factory(new Factory);
    auto req = factory->BuiltRequest();
    Calculate calculate;
    // 1、分析字节流
    std::string message;
    std::string ret;
    while (Decode(inbuffer, &message))
    {
        // 2、反序列化
        if (!req->Deserialize(message))
        {
            *error_code = false;
            return std::string();
        }
        // 3、业务逻辑
        auto resp = calculate.cal(req);
        // 4、序列化
        std::string send_str;
        resp->Serialize(&send_str);
        // 5、添加报头
        send_str = Encode(send_str);
        ret += send_str;
    }
    return ret;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "Usage : " << argv[0] << " port" << std::endl;
        return 0;
    }
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<TcpServer> svr(new TcpServer(port, Handler));
    svr->Loop();
    return 0;
}

//TcpClient.cpp
#include "Socket.hpp"
#include "Protocol.hpp"
using namespace Protocol;
const std::string opers = "+-/*%=&^|";
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << "Usage : " << argv[0] << " ip port" << std::endl;
        return 0;
    }
    uint16_t port = std::stoi(argv[2]);
    std::string ip = argv[1];
    zxws::Socket *sock = new zxws::TcpSocket();
    if (!sock->BuildConnectSocket(ip, port))
    {
        std::cout << "connect failed" << std::endl;
        return 0;
    }
    std::cout << "connect success" << std::endl;
    std::unique_ptr<Factory> factory(new Factory);
    srand(time(nullptr));
    std::string response;
    while (1)
    {
        // 1、创建消息
        int x = rand() % 100;
        int y = rand() % 100;
        char op = opers[rand() % opers.size()];
        usleep(1000);
        std::shared_ptr<Request> req = factory->BuiltRequest(x, y, op);
        req->DeBug();
        // 2、序列化
        std::string requires;
        req->Serialize(&requires);
        std::cout<<requires<<std::endl;
        // 3、添加报头
        std::string res = Encode(requires);

        // 4、发送
        sock->Send(res);

        // 5、接收
        sock->Recv(&response, 1024);
        // 6、分析字节流
        std::string message;
        if (!Decode(response, &message))
            break;

        // 7、反序列化
        auto resp = factory->BuiltResponse();
        if (!resp->Deserialize(message))
            break;
        // 打印
        std::cout << x << op << y << " = " << resp->GetResult() << " [" << resp->GetCode() << "]" << std::endl;
    }
    sock->CloseSockfd();
    return 0;
}

四、总结和扩展

当然这只是一个协议,我们以后如果想要增加功能,可以再去定义协议,只要在序列化数据的头部加上该请求想要访问哪个服务的即可,其他操作步骤和我们制定网络计算器一样。

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

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

相关文章

无人机探测技术,无人机侦测频谱仪技术实现详解

频谱仪&#xff0c;又称为频谱分析仪&#xff0c;是一种用于测量电信号频谱特性的仪器。其基本原理是通过将时域信号转换为频域信号&#xff0c;进而分析信号的频率成分、功率分布、谐波失真等参数。频谱仪利用快速傅里叶变换&#xff08;FFT&#xff09;算法&#xff0c;将采集…

13 c++版本的五子棋

前言 呵呵 这大概是 大学里面的 c 五子棋了吧 有一些 面向对象的理解, 但是不多 这里 具体的实现 就不赘述, 仅仅是 发一下代码 以及 具体的使用 然后 貌似 放在 win10 上面执行 还有一些问题, 渲染的, 应该很好调整 五子棋 #include<Windows.h> #include<io…

安规电容定义和应用

安规电容 定义 失效后&#xff0c;不会导致电击&#xff0c;不危及人身安全的电容器&#xff0c;称之为安规电容 分类 分为X电容和Y电容 X电容–跨接在电力线&#xff08;L-N&#xff09;之间的电容&#xff0c;一般选用金属薄膜电容&#xff0c;X电容有多种颜色&#xff…

VUE3----Swiper滑动切换图片

Swiper滑动切换图片 可以切换焦点图&#xff0c;兼容小程序 <template><view class"cc-swiper-container" v-if"imageList.length > 0"><swiper class"swiper":class"swiperClassName" :circular"circular&q…

Docker常用命令(镜像、容器)

一、镜像 1.1 存出镜像 1.2 载入镜像 1.3 上传镜像 二、容器 2.1 容器创建 2.2 查看容器的运行状态 ​2.3 启动容器 2.4 创建并启动容器 2.5 在后台持续运行 docker run 创建的容器 2.6 终止容器运行 2.7 容器的进入 ​2.8把宿主机的文件传入到容器内部 2.9 从容器…

C语言 | Leetcode C语言题解之第51题N皇后

题目&#xff1a; 题解&#xff1a; int solutionsSize;char** generateBoard(int* queens, int n) {char** board (char**)malloc(sizeof(char*) * n);for (int i 0; i < n; i) {board[i] (char*)malloc(sizeof(char) * (n 1));for (int j 0; j < n; j) board[i][…

Spring Cloud学习笔记(Feigh):简介,实战简单样例

这是本人学习的总结&#xff0c;主要学习资料如下 - 马士兵教育 1、Netflix Feign简介2、Open Feign的简单样例2.1、dependency2.2、代码样例 1、Netflix Feign简介 Netfilx Feign是用来帮助发送远程服务的&#xff0c;它让开发者觉得调用远程服务就像是调用本地方法一样&…

服务器数据恢复—ESXi无法识别数据存储和VMFS文件系统如何恢复数据?

服务器数据恢复环境&#xff1a; 一台某品牌服务器&#xff0c;通过FreeNAS来做iSCSI&#xff0c;然后使用两台同品牌服务器做ESXi虚拟化系统。 FreeNAS层为UFS2文件系统&#xff0c;使用整个存储建一个稀疏模式的文件&#xff0c;挂载到ESXi虚拟化系统。ESXi虚拟化系统中有3台…

怎样通过HTTP协议实现远程控制两路开关

怎样通过HTTP协议实现远程控制两路开关呢&#xff1f; 本文描述了使用HTTP协议调用HTTP接口&#xff0c;实现控制两路开关&#xff0c;两路开关可控制两路照明、排风扇等电器。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能Wi…

flutter开发实战-build apk名称及指令abiFilters常用gradle设置

flutter开发实战-build apk名称及指令abiFilters常用gradle设置 最近通过打包flutter build apk lib/main.dart --release&#xff0c;发现apk命名规则需要在build.gradle设置。这里记录一下。 一、apk命名规则 在android/app/build.gradle中需要设置 android.applicationVa…

OpenWrt上的docker容器无法访问外网解决

容器里能ping通OpenWrt的管理地址和wan口地址&#xff0c;但ping外网别的ip或域名就无法访问 简单修改设置就可以&#xff1a; Luci>网络>防火墙>转发&#xff1a;接受 ->保存应用

俩招解决vs code中文编译乱码现象

如下图所示 输出会出现乱码,引起这个情况的原因是什么呢? 本页面的编码形式可能不是 utf-8 的形式。 解决方法一 可以尝试使用命令 system("chcp 65001"); system("chcp 65001"); system("chcp 65001"); 这条命令在C程序中用于解决中文乱码问…

银行押款车远程监控系统的实际需求与特点

随着金融行业的快速发展&#xff0c;银行押款车的安全性问题日益受到重视。传统的押款车监控方式已经无法满足现代安全管理的需求&#xff0c;因此&#xff0c;一种结合先进技术的远程监控系统应运而生。本文旨在探讨在运钞车上安装车载摄像机和集成有GPS、无线4G网络传输模块的…

四数之和 ---- 双指针

题目链接 题目: 分析: 我们已经知道三数之和如何求取, 并去重了 三数之和 那么四数之和同理, 需要固定两个数a和b 然后用"双指针算法" , 只要两指针之和等于target-a-b即可同样对于四个数都要进行去重 代码: class Solution {public List<List<Integer>…

LM2576D2TR4-5G 3.0安15伏降压开关稳压器 PDF中文资料_参数_引脚图

LM2576D2TR4-5G 规格信息&#xff1a; 制造商:ON Semiconductor 产品种类:开关稳压器 RoHS:是 装置风格:SMD/SMT 封装 / 箱体:TO-263-5 输出电压:5 V 输出电流:3 A 输出端数量:1 Output 最大输入电压:45 V 拓扑结构:Buck 最小输入电压:7 V 开关频率:52 kHz 最小工作…

新书推荐机器学习大数据平台的构建、任务实现与数据治理

在大数据与机器学习日新月异的今天&#xff0c;构建稳定、安全、可扩展的数据平台已成为企业和研究机构的迫切需求。这本书应运而生&#xff0c;提供了详尽且实用的指南&#xff0c;帮助读者在云计算环境中构建、优化和治理大数据平台。 作者以清晰明了的写作风格&#xff0c;…

最小费用流相位解包裹

% test_cunwrap.m % % Matlab script to test Costantinis unwrapping % Author: Bruno Luong <brunoluong@yahoo.com> % History: % Orginal: 27-Aug-2009clear all; close all; clc; I1=double(imread(E:\zhenlmailcom-E8E745\华为家庭存储\.public_files\博士阶段\小…

与Apollo共创生态:我和Apollo七周年大会的心路历程

一、写在前面 前几天观看了Apollo七周年大会&#xff0c;给我带来了超多的惊喜&#xff0c;博主想将这份惊喜分享给大家&#xff01; 二、Apollo开放平台 Apollo开放平台秉承其核心理念——开放能力、共享资源、加速创新、持续共赢&#xff0c;致力于推动自动驾驶技术的革新…

MySQL recursive 递归

MySQL 从最内的select开始执行&#xff0c;但是同一个select clause可以在查询的结果上继续查询。 SELECT menu_id,parent_id,(SELECT m1.parent_id FROM sys_menu AS m1 WHERE m1.menu_idm.parent_id) FROM sys_menu AS m WHERE m.menu_id 89 方案1.通过recursive递归 使用…

R基本的数据管理

一&#xff0c;创建变量 创建一个数据框 > myData<-data.frame(x1c(1,2,3,4,5,6),x2c(6,5,67,8,9,0)) > myDatax1 x2 1 1 6 2 2 5 3 3 67 4 4 8 5 5 9 6 6 0增加一列为两者的和 > myData$sum<-myData$x1myData$x2 > myDatax1 x2 sum 1 1 6 …