Linux---自定义协议

news2024/11/24 16:01:18

应用层协议

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

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

请求和响应的结构体如下

// 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;
}

四、总结和扩展

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

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

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

相关文章

详解23种设计模式——单例模式

单例模式 | CoderMast编程桅杆单例模式 单例模式是最常用的设计模式之一&#xff0c;他可以保证在整个应用中&#xff0c;某个类只存在一个实例化对象&#xff0c;即全局使用到该类的只有一个对象&#xff0c;这种模式在需要限制某些类的实例数量时非常有用&#xff0c;通常全局…

不使用加减运算符实现整数加和减

文章目录 进位 进位 加粗 最近想出了不适用运算符实现加与减 首先按位与找出的是需不需要进位 按位与是两边同时为1,则为1,那么如果两边同时为1的话,是不是就该进位?所以我们用按位与来判断是否需要进位 然后再按位异或找出不同的位数 按位异或是两边不相等,也就是1 和 0的时…

LAPGAN浅析

LAPGAN 引言 在原始 GAN和CGAN中&#xff0c;还只能生成 16*16, 28*28, 32*32 这种低像素小尺寸的图片。而LAPGAN首次实现 64*64 的图像生成。与其一下子生成这么大的图像 &#xff08;包含信息量这么多&#xff09;&#xff0c;不如一步步由小到大&#xff0c;这样每一步生成…

【产品经理修炼之道】- B端产品经理之业务系统设计

很多时候&#xff0c;业务系统建设好坏决定了企业的核心竞争力。作为产品经理&#xff0c;如何建设好业务系统这种OLTP类产品&#xff1f;本文从梳理业务流程、参与业务调研和设计业务系统三个步骤&#xff0c;教大家如何做好业务系统建设。 很多人都说设计B端产品最重要的是搞…

线性代数:抽象向量空间

一、说明 有些函数系列极具线性代数的向量特征。这里谈及多项式构成函数的线性代数意义。问题是这个主题能展开多少内涵&#xff1f;请看本文的论述。 二、线性空间和向量 让我先问你一个简单的问题。什么是向量&#xff1f;为了方便起见&#xff0c;二维箭头从根本上说是平…

【UE 材质】水波纹效果

效果 模拟雨水打落在水面上的效果 步骤 1. 下载所需纹理和纹理 纹理2. 新建一个材质&#xff0c;这里命名为“M_WaterRipples” 打开“M_WaterRipples”&#xff0c;添加一个纹理采样节点&#xff0c;纹理使用第一步下载的纹理 将纹理采样节点的R通道连接到基础颜色&#x…

04_Scala网络序列化

文章目录 **1.网络****2. 序列化** 1.网络 Scala进行网络数据交互&#xff0c;使用是Java的IO类 实现案例&#xff1a;客户端连接服务器&#xff0c;向服务器发送数据&#xff1b; 1.创建两个文件&#xff0c;CLIENT&#xff0c;Server obj类型** ** Server端 2.在Server端…

2024中国航空航天暨无人机展诚邀全国相关商协会组团参展

2024中国航空航天暨无人机展诚邀全国相关商协会组团参展 2024中国航空航天暨无人机展览会诚邀全国各关联商会、协会&#xff0c;联盟、各专业会展公司、各级城市政府及关联产业园区、各关联网站报纸杂志及平台等组团参展 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民…

OpenHarmony ArkUI 实战开发—ETS 装饰器解读

前言 最近利用空闲时间在学习华为方舟开发框架&#xff08;简称&#xff1a;ArkUI&#xff09;的ets开发&#xff0c;发现在ets语言中装饰器的有着非常重要的作用&#xff0c;在写每一个自定义组件时都需要用到它&#xff0c;看到装饰器这个字眼&#xff0c;想起之前学过的设计…

偏见将如何扼杀你的人工智能/机器学习策略以及如何应对

任何类型的模型中的“偏差”都描述了模型对提示或输入数据的响应不准确的情况&#xff0c;因为它没有接受足够的高质量、多样化的数据训练来提供准确的响应。一个例子是 苹果的面部识别手机解锁功能对于肤色较深的人来说失败率明显高于肤色较浅的人。 该模型没有接受过足够多的…

分享8款安全监控/日志记录工具

安全监控工具的作用是实时监控和分析系统的安全状态&#xff0c;而日志记录工具的作用主要是记录系统的运行过程及异常信息。 关于安全监控工具&#xff0c;它通过对计算机系统、网络、应用程序和数据进行实时监控和分析&#xff0c;帮助发现和防止安全威胁和攻击。这种监控不…

2024全网最火的接口自动化测试,一看就会

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

2024信息系统、信号处理与通信技术国际会议(ICISPCT2024)

2024信息系统、信号处理与通信技术国际会议&#xff08;ICISPCT2024) 会议简介 2024国际信息系统、信号处理与通信技术大会&#xff08;ICISPCT2024&#xff09;将在青岛隆重开幕。本次会议旨在汇聚全球信息系统、信号处理和通信技术领域的专家学者&#xff0c;共同探索行业…

全网最全的接口测试教程

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 其实我觉得接口测试很简单&#xff0c;比一般的功能测试还简单&…

组合模式【结构型模式C++】

1.概述 组合模式又叫部分整体模式属于结构型模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。 2.结构 组件&#xff08;Component&#xff09;:定义了组合中所有对象的通用接口&#xff0c…

信号的调幅(AM)、调频(FM)与调相(PM)对频谱结构的影响(找找复刻电赛D题的伙伴)

0.目录 &#xff08;1&#xff09;调制与解调的基本概念 &#xff08;2&#xff09;调幅对频谱结构的影响 &#xff08;3&#xff09;调频信号幅值变化对频谱结构的影响 &#xff08;4&#xff09;调频信号频率变化对频谱结构的影响 &#xff08;5&#xff09;调幅调频信号…

Java Web 网页设计(3)

3.servlet JavaWeb——Servlet&#xff08;全网最详细教程包括Servlet源码分析&#xff09;-CSDN博客 servlet java不支持 只有Tomcat支持 使用时添加一下 servlet中最常用的两个&#xff08;固定&#xff09;方法&#xff1a; 下面我们创建一个servlet类 package com.oracle…

PE文件(三)节表

节表引入 PE文件的结构是由DOS头PE标记标准PE头可选PE头节表多个节构成的&#xff0c;如下便是一个pe文件结构图&#xff0c;它的每一段都可以被称作节 图中这么多的节在硬盘上和内存中的存储位置都由节表去管理和记录&#xff0c;而不是随意的存储。 节表相当于是一个对各个…

论文解读:Label Hallucination for Few-Shot Classification

文章汇总 动机 本文的一个思想就是&#xff1a;尽管新类的标签并不能“恰如其分”地表示基数据集中的样本&#xff0c;但是很多基数据集的样本会包含与新类中相似的对象&#xff0c;例如&#xff0c;基数据集中的老虎和新类中的猫有相似的特征&#xff0c;那么就有60%的概率将…

补充centos7软件包的方式/编译安装源码包软件/企业案例/linux进程管理/企业管理进程系列命令(企业经验)--8820字详谈

cenros7软件包的安装方式 软件包分类安装方式优缺点rpm包软件开发商编译打包&#xff0c;安装简单&#xff0c;快速软件版本可能偏低&#xff0c;安装路径是固定好的源码包自己手动编译安装并且复杂软件爸爸随意选&#xff0c;可以定制安装路径二进制包解压就可以使用不能进行…