【Linux】自定义协议——实现网络序列化和反序列化

news2024/9/20 0:59:43

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
🌎推荐文章:承接上文内容【Linux】应用层协议序列化和反序列化

在这里插入图片描述


目录

  • 👉🏻代码实现如下
    • Calculate.hpp
    • Protocol.hpp
    • Socket.hpp
    • TcpClientMain.cc
    • TcpServer.hpp
    • TcpServerMain.cc
    • 实现效果

👉🏻代码实现如下

代码目录:
在这里插入图片描述
此次代码主要变动如下:
🔥TcpServerMain.cc:
1.HanlderRrequest:
a.读取报文
b.分析是否是一个完整的报文
c.当读到完整的报文后,进行反序列化
d.业务处理(计算器)
e.将计算结果的报文进行序列化
d.将报文再度Encode再度封装加上长度和\n
7.发送报文

🔥Calculate:

🔥TcpServer.hpp:
func_t
1.ThreadRun
a.读取报文
b.报文处理
c.发送处理

🔥TcpClientMain.cc:
1.构建请求,
2.对请求进行序列化
3.添加自描述报头
4.发送请求
5.读取响应
6.报文解析
7.创建response "result code"并进行反序列化
8.输出结果

Calculate.hpp

#pragma once

#include<iostream>
#include<memory>
#include"Protocol.hpp"

namespace CalCulateNS
{
    enum
    {
        Success = 0,
        DivZeroErr,
        ModZeroErr,
        UnKnowOper
    };
    class CalCulate
    {
     public:
         CalCulate() {}
        
        //该函数对传进来的需求进行计算处理,然后转换为返回格式转出
         shared_ptr<Protocol::Response> Cal(shared_ptr<Protocol::Request> req)
         {
            //1.首先需要创建回应对象指针
            shared_ptr<Protocol::Response> resp = factory.BuildResponse();
            resp->SetCode(Success);//创建成功 

            //2.接下来就是对各种操作符进行选择计算
             switch (req->GetOper())
            {
            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(DivZeroErr);//除0错误
                }
                else
                {
                    resp->SetResult(req->GetX() / req->GetY());
                }
            }
            break;
            case '%':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(ModZeroErr);
                }
                else
                {
                    resp->SetResult(req->GetX() % req->GetY());
                }
            }
            break;
            default:
                resp->SetCode(UnKnowOper);//未知错误
                break;
            }

            return resp;

         }
        ~CalCulate(){}
     private:
     Protocol::Factory factory;
    };
}

Protocol.hpp

#pragma once

#include<iostream>
#include<string>
#include<memory>
using namespace std;

namespace Protocol
{
    const string ProtSep = " ";//分隔符
    const string LineBreakSep = "\n";//换行符

    //封装报文:"len\nx op y\n"
    string Encode(const string& message)
    {
        string len = to_string(message.size());
        string package = len+LineBreakSep+message+LineBreakSep;
        return package;
    }


    //解析报文
    bool Decode(string& package,string* message)
    {
        //1.第一步我们要检查该报文是否有边界\n处理
        auto pos = package.find(LineBreakSep);
        if(pos==string::npos) return false;
        //2.第二步我们要判断有效载荷的长度是否如报头len中所说一样长
        string len = package.substr(0,pos);
        int messagelen = stoi(len);
        int totallen = len.size()+messagelen+2*LineBreakSep.size();//这里是整个报文的长度(报头加边界符加有效载荷)
        if(package.size()<totallen) return false;

        //3.如果上述没有问题,说明此时package内部一定有一个完整的报文了
        //我们现在就能对其解析,得到有效载荷 
        *message = package.substr(pos+LineBreakSep.size(),messagelen);
        package.erase(0,totallen);//package已经利用完,清空
        return true;
    }
    class Request
{
public:
    Request()
    :_data_x(0),_data_y(0),_oper(0)
    {}
    Request(int x,int y,char op)
    :_data_x(x),_data_y(y),_oper(op)
    {}


    void Inc()
    {
        _data_x++;
        _data_y++;
    }
    void Debug()
    {
        cout<<"_data_x: "<<_data_x<<endl;
        cout<<"_data_y: "<<_data_y<<endl;
        cout<<"_oper: "<<_oper<<endl;
    }
    bool Serialize(string* out)//序列化
    {
        *out = to_string(_data_x)+ProtSep+_oper+ProtSep+to_string(_data_y);
        return true;
    }
    bool Deserialize(string& in)//反序列化
    {
        //1.先找到操作符两边的分隔符
        auto left = in.find(ProtSep);
        if(left==string::npos) return false;//找不到则说明不是完整报文,失败
        auto right = in.rfind(ProtSep);
        if(right==string::npos) return false;
        //2.将报文中的x和y以及操作符都提取出来赋值 

        _data_x = stoi(in.substr(0,left));
        _data_y = stoi(in.substr(right+ProtSep.size()));
        string oper = in.substr(left+ProtSep.size(),right-(left+ProtSep.size()));

        if(oper.size()!=1) return false;//操作数长度只能为1
        _oper = oper[0];
        return true;
    }

    //获取_data_x、_data_y、_oper的外部接口
    int GetX(){return _data_x;}
    int GetY(){return _data_y;}
    char GetOper() {return _oper;}

private:
        // _data_x _oper _data_y
        // 报文的自描述字段
        // "len\nx op y\n" : \n不属于报文的一部分,约定
        // 很多工作都是在做字符串处理!
    int _data_x;
    int _data_y;
    char _oper;//操作数
};

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

    }
    bool Serialize(string* out)
    {
        *out = to_string(_result) + ProtSep + to_string(_code);
        return true;

    }
    bool Deserialize(string& in)
    {
        //1.找分隔符
        auto pos = in.find(ProtSep);
        if(pos==string::npos) return false;

        //2.提取
        _result = stoi(in.substr(0,pos));
        _code = stoi(in.substr(pos+ProtSep.size()));

        return true;
    }

    //设置result和code的值
    void SetResult(int res) { _result = res; }
    void SetCode(int code) {_code = code; }

    //提供result和code的外部接口
    int GetResult() { return _result; }
    int GetCode() { return _code; }
private:
// "_result _code"  序列
    int _result;
    int _code;
};

//工厂模式,建造类设计模式,直接返回指针对象
class Factory
{
public:
    shared_ptr<Request> BuildRequest()
    {
        shared_ptr<Request> req = make_shared<Request>();
        return req;
    }
    
       shared_ptr<Request> BuildRequest(int x,int y,char op)
       {
        shared_ptr<Request> req = make_shared<Request>(x,y,op);
        return req;
    }

    shared_ptr<Response> BuildResponse()
    {
        shared_ptr<Response> resp = make_shared<Response>();

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

        return resp;
    }

};
}

Socket.hpp

#pragma once 

#include<iostream>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define Convert(addrptr) ((struct sockaddr*)addrptr)

using namespace std;

namespace Net_Work
{
    const static int defaultsockfd = -1;
    const int backlog = 5;
    enum
    {
        SocketError = 1,
        BindError,
        ListenError,
    };
    //封装一个基类:Socket接口类
    class Socket
    {
    public:
        virtual ~Socket(){}
        virtual void CreateSocketOrDie() = 0;//创建一个套接字
        virtual void BindSocketOrDie(uint16_t port) = 0;//套接字进行绑定网络信息
        virtual void ListenSocketOrDie(int backlog) = 0;//进行监听
        virtual Socket* AcceptConnection(string * peerip,uint16_t* peerport)=0;//接收连接,并返回一个新的套接字
        virtual bool ConnectServer(string& peerip,uint16_t peerport)=0;//连接服务端
        virtual int GetSockFd() = 0;//返回套接字描述符
        virtual void SetSockFd(int sockfd) = 0;//
        virtual void CloseSocket() = 0;//关闭套接字

        virtual bool Recv(string *buffer,int size) = 0;//用来接收信息
        virtual void Send(string& send_str) = 0;//用来发送信息

    public:
        void BuildListenSocketMethod(uint16_t port,int backlog)//创建一个监听服务
        {
            //1.创建套接字
            CreateSocketOrDie();
            //2.套接字进行绑定网络信息
            BindSocketOrDie(port);
            //3.开始监听
            ListenSocketOrDie(backlog);
        }
        bool BuildConnectSocketMethod(string& serverip,uint16_t& serverport)//创建一个连接服务
        {
            //1.创建套接字
            CreateSocketOrDie();
            return ConnectServer(serverip,serverport);

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

    //实现Tcp套接字
    class TcpSocket:public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd )
        :_sockfd(sockfd)
        {

        }
        ~TcpSocket(){}
        /
       
         void CreateSocketOrDie() override//创建一个套接字
         {
            _sockfd = socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd<0)
                exit(SocketError);
         }

         void BindSocketOrDie(uint16_t port) override//套接字进行绑定网络信息
         {
            //本地网络信息初始化
            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(port);

            //开始绑定
            int n = bind(_sockfd,Convert(&local),sizeof(local));
            if(n<0) exit(BindError);
         }
         void ListenSocketOrDie(int backlog) override//进行监听
         {
            int n = listen(_sockfd,backlog);
            if(n<0) exit(ListenError);
         }
        Socket* AcceptConnection(string * peerip,uint16_t* peerport)override//接收连接
        {
            struct sockaddr_in peer;//用来存储客户端的地址信息
            socklen_t len = sizeof(peer);
            int newsockfd = accept(_sockfd,Convert(&peer),&len);
            if(newsockfd<0)
                return nullptr;

            *peerport = ntohs(peer.sin_port);//网络序列本地化
            *peerip = inet_ntoa(peer.sin_addr);

            Socket* s = new TcpSocket(newsockfd);
            return s;
        }
         bool ConnectServer(string& serverip,uint16_t serverport)override//连接服务端
         {
            struct sockaddr_in server;//存储服务端的地址信息
            memset(&server,0,sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(serverport);
            server.sin_addr.s_addr = inet_addr(serverip.c_str());//ip网络字节序化,4字节

            int n = connect(_sockfd,Convert(&server),sizeof(server));
            if(n==0)
                return true;
            else
                return false;
         }
         int GetSockFd() override//返回套接字描述符
         {
            return _sockfd;
         }
         void SetSockFd(int sockfd) override//
         {
            _sockfd = sockfd;
         }
         void CloseSocket() override//关闭套接字
         {
            if(_sockfd>defaultsockfd)
                close(_sockfd);
         }

          bool Recv(std::string *buffer, int size) override
        {
            char inbuffer[size];
            ssize_t n = recv(_sockfd, inbuffer, size-1, 0);
            if(n > 0)
            {
                //接收到了
                inbuffer[n] = 0;
                *buffer += inbuffer; // buffer可能是历史上所有发送来的消息,拼接在buffer后面
                return true;
            }
            else if(n == 0) return false;
            else return false;
        }
        void Send(std::string &send_str) override
        {
            // 多路转接我们再统一说
            send(_sockfd, send_str.c_str(), send_str.size(), 0);
        }
    private:
        int _sockfd;
    };
};

TcpClientMain.cc

#include "Protocol.hpp"
#include "Socket.hpp"
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <unistd.h>

using namespace Protocol;

// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << "Usage : " << argv[0] << " serverip serverport" << std::endl;
        return 0;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    Net_Work::Socket *conn = new Net_Work::TcpSocket();
    if (!conn->BuildConnectSocketMethod(serverip, serverport))
    {
        std::cerr << "connect " << serverip << ":" << serverport << " failed" << std::endl;
    }
    std::cout << "connect " << serverip << ":" << serverport << " success" << std::endl;

    std::unique_ptr<Factory> factory = std::make_unique<Factory>();

    srand(time(nullptr) ^ getpid());
    const std::string opers = "+-*/%^=&";
    while (true)
    {
        // 1. 构建一个请求,遵守协议
        int x = rand() % 100; //[0, 99]
        usleep(rand() % 7777);
        int y = rand() % 100; //[0,99]
        char oper = opers[rand() % opers.size()];
        std::shared_ptr<Request> req = factory->BuildRequest(x, y, oper);

        // 2. 对请求进行序列化
        std::string requeststr;
        req->Serialize(&requeststr); //

        std::cout << requeststr << std::endl;

        // for test
        std::string testreq = requeststr;
        testreq += " ";
        testreq += "= ";

        // 3. 添加自描述报头
        requeststr = Encode(requeststr);
        std::cout << requeststr << std::endl;

        // 4. 发送请求
        conn->Send(requeststr);
        std::string responsestr;
        while (true)
        {
            // 5. 读取响应
            if(!conn->Recv(&responsestr, 1024)) break;
            // 6. 报文进行解析
            std::string response;
            if (!Decode(responsestr, &response))
                continue; // 我就不连续的处理了

            // 7.response "result code"
            auto resp = factory->BuildResponse();
            resp->Deserialize(response);

            // 8. 得到了计算结果,而且是一个结构化的数据
            std::cout << testreq << resp->GetResult() << "[" << resp->GetCode() << "]" << std::endl;
            break;
        }
        sleep(1);
    }

    conn->CloseSocket();
    return 0;
}

TcpServer.hpp

#pragma once

#include"Socket.hpp"
#include<pthread.h>
#include<functional>

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

class TcpServer;

class ThreadData
{
public:
    ThreadData(TcpServer* tcp_this,Net_Work::Socket* sockp)
    :_this(tcp_this),_sockp(sockp)
    {}
public:
    TcpServer* _this;//TcpServer的指针对象
    Net_Work::Socket* _sockp;//套接字指针对象
};


class TcpServer
{
public:
    TcpServer(uint16_t port,func_t handler_request)
    :_port(port),_listensocket(new Net_Work::TcpSocket()),_hanlder_request(handler_request)
    {
        _listensocket->BuildListenSocketMethod(_port,Net_Work::backlog);//开启监听事务
    }
    static void * ThreadRun(void* args)//因为pthread_create要求方法参数中的参数必须只有一个void*
    //所以必须变为静态,否则成员函数第一个参数默认隐式为this指针
    {
        //因为执行的是多线程,这里我们也没有封装线程的自动回收
        //所以为了不发生线程阻塞,我们要让当前线程与主线程分离,不影响主线程,并且自己做完任务自己回收
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
       
        string inbufferstream;//用来存储Recv到的报文
        while(true)
        {
            bool ok = true;
            //1.读取报文
            if(!td->_sockp->Recv(&inbufferstream,1024)) break;//如果读取报文失败,则退出

            //2.对报文内容进行处理,并接收处理后的返回值
            string send_string = td->_this->_hanlder_request(inbufferstream,&ok);

            if(ok)
            {
                //如果处理的ok,则发送数据给客户端
                if(!send_string.empty())
                {
                    td->_sockp->Send(send_string);
                }
            }
            else
                break;
        }


        td->_sockp->CloseSocket();//关闭accept的新套接字
        delete td->_sockp;//销毁指针
        delete td;
        return nullptr;
    }
    void Loop()
    {
         while(true)
        {
            string peerip;
             uint16_t peerport;
            Net_Work::Socket* newsocket = _listensocket->AcceptConnection(&peerip,&peerport);//接收客户端信息
            if(newsocket==nullptr) continue;
                 cout<<"获取一个新连接,sockfd:"<<newsocket->GetSockFd()<<"client info: "<<peerip<<" "<<peerport<<endl;

            //用完后关闭newsocket
            //newsocket->CloseSocket();   

            //使用多线程进行处理任务
            pthread_t tid;
            ThreadData* td = new ThreadData(this,newsocket);
            pthread_create(&tid,nullptr,ThreadRun,td);//线程创建并执行相对应任务
         }
    }

    ~TcpServer()
    {
        delete _listensocket;
    }

private:
    uint16_t _port;
    Net_Work::Socket* _listensocket;

public:
    func_t _hanlder_request;//request执行方法

};

TcpServerMain.cc

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

//包含外部头文件
#include"Protocol.hpp"
#include"Socket.hpp"
#include"TcpServer.hpp"
#include"Calculate.hpp"


using namespace Net_Work;
using namespace Protocol;
using namespace CalCulateNS;


//HandlerRequest进行字节流数据解析,和调用业务处理方法得到处理结果返回
string  HandlerRequest(string& inbufferstream,bool * error_code)
{
    *error_code = true;
    //1.创建一个计算器对象,待会用来处理业务
        CalCulate calculte;
    
    //2.构建需求对象:用来进行将_data_x _oper _data_y进行序列化和反序列化
    unique_ptr<Factory> factory = make_unique<Factory>();//记得后面加()!!!
    shared_ptr<Request> req = factory->BuildRequest();

    string total_resp_string;//用来存储回应字符串
    string message;//用来存储报文解析后格式后的_data_x _oper _data_y

    //3.分析字节流,看是否有一个完整的报文
    while(Decode(inbufferstream,&message))
    {
        printf("message[%s] is waiting analysis\n",message.c_str());
        //4.此时Decode解析成功,我一定得到了一个完整的报文
        //接下来进行反序列化
         if (!req->Deserialize(message))
        {
            std::cout << "Deserialize error" << std::endl;
            *error_code = false;
            return std::string();
        }
        std::cout << "Deserialize success" << std::endl;

        //5.进行计算业务处理
        shared_ptr<Response> resp = calculte.Cal(req);

        //6.处理完后,我们要将结果也要序列化,因为我们还要发送回客户端!
         std::string send_string;
        resp->Serialize(&send_string); // 序列化为"result code"
        
        //7.构建完成的字符串级别的响应报文
        send_string = Encode(send_string);
        total_resp_string += send_string;
    }
    return total_resp_string;
}

int main(int argc,char* argv[])
{
     if(argc != 2)
    {
        cout << "Usage : " << argv[0] << " port" << std::endl;
        return 0;
    }
    uint16_t  localport = stoi(argv[1]);

    unique_ptr<TcpServer> svr (new TcpServer(localport,HandlerRequest));//unique_ptr只能支持移动构造

    svr->Loop();//server开始不断获取新连接
    return 0;
}

实现效果

在这里插入图片描述


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

企业常用Linux三剑客awk及案例/awk底层剖析/淘宝网cdn缓存对象分级存储策略案例/磁盘知识/awk统计与计算-7055字

高薪思维&#xff1a; 不愿意做的事情:加班&#xff0c;先例自己在利他 生活中先利他人在利自己 感恩&#xff0c;假设别人帮助过你&#xff0c;先帮助别人&#xff0c;感恩境界 awk三剑客老大 find其实也算是一种新的第四剑客 find 查找文件 查找文件&#xff0c;与其他命令…

【UnityShader】图片圆角

1.需求 我们在开发的时候&#xff0c;有时候一些按钮或者菜单栏的边角是直角的需要改成圆角&#xff0c;但是让美术重新绘制耽误时间不说也确实没必要&#xff0c;这个时候我们不妨使用一个简单的shader去解决这个问题&#xff0c;下面我们就讲讲这个shader要如何实现。 需求1…

设计模式之观察者模式(优先使用对象组合的原则)的C++实现

观察者模式又称订阅者发布者模式&#xff0c;本篇介绍主要是利用对象组合大于类继承的设计模式原则实现订阅发布模式&#xff0c;这种设计的优点是想订阅数据的类不需要继承订阅者类的抽象类&#xff0c;减少了一层类的继承&#xff1b;当然&#xff0c;具体情况需要可根据需求…

在ios设备上运行Unity Profiler

久违了朋友们。 最近基于Unity 2021.3 和AR Foundation开发了个应用&#xff0c;需要在ipad上实际运行时查看程序的各项指标功耗。 于是乎&#xff0c;我尝试跟随者官方教程来实时调试&#xff0c;现在附上一些心得。 按照官方的三步走&#xff0c;Build and Run理论上会自动…

42岁TVB男艺人曾靠刘德华贴钱出道,苦熬10年终上位

张颕康在无线&#xff08;TVB&#xff09;电视打滚多年&#xff0c;近年在《逆天奇案》第一、二辑凭扎实演技为人留下印象。他还是圈中出名的「爱妻号」&#xff0c;日前在访问期间&#xff0c;张颕康三句不离多谢太太。 较年长的观众或会记得&#xff0c;张颕康初出道以「刘德…

边缘计算智能分析网关V4地面垃圾AI检测算法介绍及场景应用

在传统的卫生监管场景中&#xff0c;无法及时发现地面遗留的垃圾&#xff0c;通过人工巡逻的方式需要大量的人力、物力和时间&#xff0c;而且效率不高&#xff0c;并存在一定的滞后性&#xff0c;而采用地面垃圾AI检测算法则可以大大提高监管效率。 TSINGSEE青犀AI智能分析网…

骑砍2霸主MOD开发(6)-使用C#-Harmony修改本体游戏逻辑

一.C#-Harmony反射及动态注入 利用C#运行时环境的反射原理,实现对已加载DLL,未加载DLL中代码替换和前置后置插桩. C#依赖库下载地址:霸王•吕布 / CSharpHarmonyLib GitCodehttps://gitcode.net/qq_35829452/csharpharmonylib 根据实际运行.Net环境选择对应版本的0Harmony.dll…

【编译原理】03语法分析

1&#xff0c;语法分析的若干问题 1.1 语法分析器的作用 编译器前端的重要组成部分&#xff1a; (1) 根据词法分析器提供的记号流&#xff0c;为语法正确的输入构造分析树(或语法树)。 (2) 检查输入中的语法(可能包括词法)错误&#xff0c;并调用出错处理器进…

MyBatis 核心配置讲解(上)

大家好&#xff0c;我是王有志&#xff0c;一个分享硬核 Java 技术的互金摸鱼侠。 前两篇的文章中我们分别介绍了 MyBatis 和 MyBaits 的应用组成&#xff0c;到这里基础篇的内容就结束了。 从今天开始&#xff0c;我们正式进入 MyBatis 学习的第二阶段&#xff1a;MyBatis 的…

插值与重采样在AI去衣技术中的关键作用

在人工智能&#xff08;AI&#xff09;的众多应用中&#xff0c;去衣技术作为一种新兴的图像处理技术&#xff0c;逐渐引起了广泛关注。这项技术不仅涉及复杂的计算机视觉和深度学习算法&#xff0c;还需要对图像处理中的插值与重采样技术有深入的理解。本文将详细探讨插值与重…

【笔试训练】day7

1.在字符串中找出连续最长的数字串 思路&#xff1a; 简单双指针&#xff0c;随便怎么暴力 代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include<string> using namespace std;int main() {string str;cin >> str;int ans …

微服务之SpringCloud AlibabaNacos服务注册和配置中心

一、概述 1.1注册中心原理 在微服务远程调用的过程中&#xff0c;包括两个角色&#xff1a; 服务提供者&#xff1a;提供接口供其它微服务访问&#xff0c;比如item-service 服务消费者&#xff1a;调用其它微服务提供的接口&#xff0c;比如cart-service 在大型微服务项目…

Laya2.13.3 Texture和Teture2D的关系,怎样将Texture2D转换为Texture。

Texture是是纹理处理类&#xff0c;Sprite和Image上显示的图像都是经Texture处理的&#xff0c; Texture2D是3d模型纹理贴图的处理类&#xff0c;用于显示3D模型的纹理细节。 如何将Textture2D转换为Texture&#xff0c;Texture的API接口如下&#xff1a; 可以看到Texture首先…

STM32 | USART实战案例

STM32 | 通用同步/异步串行接收/发送器USART带蓝牙(第六天)随着扩展的内容越来越多,很多小伙伴已经忘记了之前的学习内容,然后后面这些都很难理解。STM32合集已在专栏创建,方面大家学习。1、通过电脑串口助手发送数据,控制开发板LED灯 从题目中可以挖掘出,本次使用led、延…

vscode 配置verilog环境

一、常用的设置 1、语言设置 安装如下插件&#xff0c;然后在config 2、编码格式设置 解决中文注释乱码问题。vivado 默认是这个格式&#xff0c;这里也设置一样。 ctrl shift p 打开设置项 3、插件信任区设 打开一个verilog 文件&#xff0c;显示是纯本文&#xff0c;没…

HarmonyOS开发环境搭建 移动开发 鸿蒙开发 ArkTS

&#x1f4dc;目录 &#x1f4a1; 环境搭建 &#x1f680;安装nodejs &#x1f935;安装ohpm &#x1f354;安装SDK &#x1f4a5;Emulator安装 &#x1f336;️新建ArkTs项目 &#x1f3c6;️ArkTS语言 ✨️基本语法 &#x1f388; 声明式UI描述 &#x1f371;组件 …

使用spring boot集成shardingsphere分库分表简易测试

根据如下pom整上一个spring-boot项目&#xff0c;spring-boot版本用2.3.5&#xff0c;shardingsphere用5.1.1。 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://ww…

【KingSCADA】通过地址引用和弹窗模板实现设备控制

当相同的设备过多时&#xff0c;要做很多相同的弹窗&#xff0c;这种情况下可以通过地址引用和弹窗模板实现设备控制。 1.变量创建 2.画面开发 以阀门控制为例&#xff0c;只需要做一个阀门控制界面模板 3.地址引用 # 4.实现效果

Linux基本命令之正则表达式(转义字符)

一&#xff1a;查看二进制文件 strings 命令&#xff1a;strings 文件名 生成链接文件 ln 命令&#xff1a;ln 选项 源文件(f1) 链接文件&#xff08;f2&#xff09; 软连接&#xff1a;eg:ln -s f1 f2 软链接不能跨分区链接&#xff0c;但可以在同一分区的不同目录下链接…

小型架构实验模拟

一 实验需求 二 实验环境 22 机器&#xff1a; 做nginx 反向代理 做静态资源服务器 装 nginx keepalived filebeat 44机器&#xff1a; 做22 机器的备胎 装nginx keepalived 99机器&#xff1a;做mysql的主 装mysqld 装node 装filebeat 77机器&#xff1a;做mysq…