【网络】协议与序列化反序列化

news2024/11/25 11:26:53

目录

一、相关概念

二、自定义协议

三、编写服务器

四、编写客户端

五、JSON

六、补充内容


一、相关概念

 在《网络编程套接字》中,我们实现了服务器与客户端之间字符串的通信。但是更多的时候,需要传输的不仅仅是字符串,而是结构化的数据。

 我们可以定义结构体来表示需要交互的信息,发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化回结构体。这个过程叫做 "序列化" 和 "反序列化"。

 根据某种约定,使一端发送时构造的数据,在另一端能够正确的进行解析。这种约定就是应用层协议。

二、自定义协议

现在我们自己定义一个协议,用来实现服务器版本的计算器。

 服务器与客户端在互相收发消息时,并不是直接把数据发送给对方,直接从对方处读取数据。而是经过了一系列层状结构。

 在传输层,TCP协议有自己的发送缓冲区和接收缓冲区。用户层也有一个缓冲区,用户输入数据是输入到用户层的缓冲区的。

 以写入为例,用户向用户层的缓冲区输入了一份数据,调用 write 函数,本质是把用户层缓冲区的数据拷贝到传输层TCP协议的发送缓冲区中。对方调用 read 函数,本质上是把TCP协议的接收缓冲区中的数据拷贝到用户层的缓冲区。

 用户层调用write函数,把数据拷贝完成后就返回了。数据什么时候发送到对方,怎么发,都由TCP决定,因此TCP协议被称为传输控制协议。TCP发送数据的本质,是把自己发送缓冲区里的数据,经过网络拷贝到对方的接收缓冲区中,所以TCP通信的本质,也是拷贝。TCP协议是全双工的。

 为了保证每一次读取,都能从接收缓冲区中恰好读取到一段完整的、独立的报文字符串,这需要使用一些方法,比如在字符串前面加上独立报文字符串的长度。相当于加上报头。

//Util.hpp 用于做数据间转换
#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <cstring>
using namespace std;

//工具类用来做一些转换
class Util
{
public:
    //输入:const&
    //输出:*
    //输入输出:&

    static bool StringSplit(const std::string& str, const std::string& sep, std::vector<std::string>* result)
    {
        // 10 + 20

        size_t start = 0;
        while(start < str.size())
        {
            auto pos = str.find(sep, start);
            if(pos == string::npos)
                break;
            result->push_back(str.substr(start, pos - start)); //前闭后开区间,尾减首刚好是元素个数
            
            //位置的重新加载
            start = pos + sep.size();
        }

        if(start < str.size())
            result->push_back(str.substr(start));

        return true;
    }

    static int toInt(const string& s)
    {
        return atoi(s.c_str());
    }
};

//Protocol.hpp 自定义协议
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <vector>

#include "Util.hpp"

//给网络版本计算机定制协议
namespace Protocol_ns
{
    #define SEP " "
    #define SEP_LEN strlen(SEP) //一定不能使用sizeof
    #define HEADER_SEP "\r\n"
    #define HEADER_SEP_LEN strlen("\r\n")

    //"长度"\r\n"_x _op _y"\r\n
    //请求/响应 = 报头\r\n有效载荷\r\n
    string AddHeader(const string& str)
    {
        string s = to_string(str.size());
        s += HEADER_SEP;
        s += str;
        s += HEADER_SEP;

        return s;
    }

    //"7"\r\n"10 + 20"\r\n => "10 + 20"
    string RemoveHeader(const string& str, int len)
    {
        auto pos = str.find(HEADER_SEP);
        
        string package;
        package = str.substr(pos + HEADER_SEP_LEN, len);  //有效载荷

        return package;
    }

    int ReadPackage(int sock, string& inbuffer, string* package)
    {
        //边读取,边分析
        //边读取
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof(buffer - 1), 0);
        if(s <= 0)
            return -1;
        buffer[s] = 0;
        inbuffer += buffer;

        //边分析, "7"\r\n"10 + 20"\r\n
        auto pos = inbuffer.find(HEADER_SEP);
        if(pos == string::npos)
            return 0;
        string lenStr = inbuffer.substr(0, pos); //获取头部字符串
        int len = Util::toInt(lenStr);
        int targetPackageLen = lenStr.size() + len + 2 * HEADER_SEP_LEN;

        if(inbuffer.size() < targetPackageLen)
            return 0;

        *package = inbuffer.substr(0, targetPackageLen);  //数据报
        inbuffer.erase(0, targetPackageLen); //从inbuffer中直接移除了整个报文

        return len;
    }

    //Request && Response 都要提供序列化和反序列化的功能

    class Request
    {
    public:
        Request()
        {}

        Request(int x, int y, char op)
        :_x(x)
        ,_y(y)
        ,_op(op)
        {}

        //struct->string
        bool Serialize(std::string* outStr)
        {
            *outStr = "";
            std::string x_string = std::to_string(_x);
            std::string y_string = std::to_string(_y);

            //手动序列化
            *outStr = x_string + SEP + _op + SEP + y_string;

            return true;
        }

        //string->struct
        bool Deserialize(const std::string& inStr)
        {
            //inStr: 10 + 20 => [0] = 10, [1] = +, [2] = 20
            std::vector<std::string> result;
            Util::StringSplit(inStr, SEP, &result);

            if(result.size() != 3) //一定可以切割成三部分
                return false;
            if(result[1].size() != 1) //中间部分一定只有一个字符
                return false;
            _x = Util::toInt(result[0]);
            _y = Util::toInt(result[2]);

            _op = result[1][0];
        }

        ~Request()
        {}

    public:
        int _x;
        int _y;
        char _op;
    };

    class Response
    {
    public:
        Response()
        {}

        Response(int result, int code)
        :_result(result)
        ,_code(code)
        {}
        
        //struct->string
        bool Serialize(std::string* outStr)
        {
            //_result _code
            *outStr = "";
            string res_string = to_string(_result);
            string code_string = to_string(_code);

            *outStr = res_string + SEP + code_string;
            return true;
        }

        //string->struct
        bool Deserialize(const std::string& inStr)
        {
            std::vector<std::string> result;
            Util::StringSplit(inStr, SEP, &result);           

            if(result.size() != 2)
                return false;

            _result = Util::toInt(result[0]);
            _code = Util::toInt(result[1]);

            return true;
        }

        ~Response()
        {}
    public:
        int _result;
        int _code;
    };
}

三、编写服务器

下面使用我们自定义的协议实现网络计算器的服务器:

//sock.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>

#include "error.hpp"
#include "log.hpp"

static const int gbacklog = 32;
static const int defaultfd = -1;

class Sock
{
public:
    Sock()
    :_sock(defaultfd)
    {

    }

    void Socket()
    {
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_sock < 0)
        {
            logMessage(FATAL, "socket error, code: %d, errstring: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
    }

    void Bind(const uint16_t& port)
    {
        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;

        if(bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind error, code: %d, errstring: %s", errno, strerror(errno));
            exit(BIND_ERR);           
        }
    }

    void Listen()
    {
        if(listen(_sock, gbacklog) < 0)
        {
            logMessage(FATAL, "listen error, code: %d, errstring: %s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
    }

    int Accept(string* clientip, uint16_t* clientport)
    {
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        int sock = accept(_sock, (struct sockaddr*)&temp, &len);
        if(sock < 0)
        {
            logMessage(WARNING, "accept error, code: %d, errstring: %s", errno, strerror(errno));
        }
        else
        {
            *clientip = inet_ntoa(temp.sin_addr);
            *clientport = ntohs(temp.sin_port);
        }
        return sock;
    }

    int Connect(const string& serverip, const uint16_t& serverport)
    {
        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());

        // int n = connect(sock, (struct sockaddr*)&server, sizeof(server));

        // if(n < 0)
        // {
        //     logMessage(FATAL, "connect error, code: %d, errstring: %s", errno, strerror(errno));
        //     exit(CONNECT_ERR);            
        // }
        return connect(_sock, (struct sockaddr*)&server, sizeof(server));
    }

    int Fd()
    {
        return _sock;
    }

    ~Sock()
    {
        if(_sock != defaultfd)
            close(_sock);
    }
private:
    int _sock; //只定义一个_sock,解释权归调用者所有
               //server调用就是listensock
               //client调用就是客户端的sock
};


//TcpServer.hpp
#pragma once

#include <iostream>
#include <pthread.h>
#include <functional>

#include "Sock.hpp"
#include "Protocol.hpp"

using namespace Protocol_ns;

using func_t = std::function<Response(const Request &)>;
class TcpServer;
class ThreadData
{
public:
    ThreadData(int s, string &clientip, uint16_t clientport, TcpServer *p)
        : _sock(s), _clientip(clientip), _clientport(clientport), _tsvrp(p)
    {
    }

    ~ThreadData()
    {
    }

public:
    int _sock;
    string _clientip;
    uint16_t _clientport;
    TcpServer *_tsvrp;
};

class TcpServer
{
public:
    TcpServer(func_t func, uint16_t port)
        : _func(func), _port(port)
    {
    }

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        logMessage(INFO, "init server done, listensock: %d", _listensock.Fd());
    }

    void Start()
    {
        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sock = _listensock.Accept(&clientip, &clientport);
            if (sock < 0)
                continue;

            logMessage(DEBUG, "get a new client, client info : [%s : %d]", clientip.c_str(), clientport);

            pthread_t tid;
            ThreadData *td = new ThreadData(sock, clientip, clientport, this);
            pthread_create(&tid, nullptr, ThreadRoutine, td);
        }
    }

    static void *ThreadRoutine(void *args)
    {
        pthread_detach(pthread_self());

        ThreadData *td = static_cast<ThreadData *>(args);

        // logMessage(DEBUG, "thread runing ");

        td->_tsvrp->ServiceIO(td->_sock, td->_clientip, td->_clientport);

        delete td;
        return nullptr;
    }

    void ServiceIO(int sock, const string &clientip, const uint16_t port)
    {
        string inbuffer; // 用来保存每一次读取到的数据,以防这次读取的数据没有被返回,下一次再读取时,这一段数据就被覆盖了(这个数据不一定是符合协议格式的)
        while (1)
        {
            // 1.读取报文
            string package;
            int n = ReadPackage(sock, inbuffer, &package);
            if (n == -1)
                break;
            else if (n == 0)
                continue;
            else
            {

                // 2.提取有效载荷
                package = RemoveHeader(package, n);

                // 3.对读取到的数据做反序列化
                Request req;
                req.Deserialize(package);

                // 4.直接提取用户的请求数据,进行处理
                Response resp = _func(req); // 业务逻辑

                // 5.给用户返回响应,进行序列化,形成可发送字符串
                string send_string;
                resp.Serialize(&send_string);

                // 6.添加报头
                send_string = AddHeader(send_string);

                //7.发送
                send(sock, send_string.c_str(), send_string.size(), 0);
            }
        }
        close(sock);
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    Sock _listensock;
    func_t _func;
};


//CalcolatorServer.cc
#include "TcpServer.hpp"
#include <memory>

Response calculate(const Request& req)
{
    Response resp(0, 0);
    switch(req._op)
    {
        case '+':
            resp._result = req._x + req._y; 
            break;
        case '-': 
            resp._result = req._x - req._y; 
            break;
        case '*':
            resp._result = req._x * req._y; 
            break;
        case '/':
            if(req._y == 0)
                resp._code = 1;
            else
                resp._result = req._x / req._y; 
            break;
        case '%':
            if(req._y == 0)
                resp._code = 2;
            else
                resp._result = req._x % req._y; 
            break;
        default:
            resp._code = 3;
            break;
    }

    return resp;
    
}

//./calserver port
int main(int args, char* argv[])
{
    if(args != 2)
    {
        userage(argv[0]);
        exit(USAGE_ERR);
    }

    uint16_t port = atoi(argv[1]);

    std::unique_ptr<TcpServer> tsvr(new TcpServer(calculate, port));
    tsvr->InitServer();
    tsvr->Start();

    return 0;
}

四、编写客户端

//CalculatorClient.cc

#include <iostream>
#include <string>

#include "Sock.hpp"
#include "Protocol.hpp"

using namespace std;
using namespace Protocol_ns;

static void userage(string proc)
{
    cout << "Usage:\n\t" << proc << " serverip serverport\n"
         << endl;
}

enum
{
    LEFT,
    OPER,
    RIGHT
};

Request Parseline(const string &line)
{
    string left, right;
    char op;
    int status = LEFT;

    int i = 0;
    while(i < line.size())
    {
        switch (status)
        {
        case LEFT:
            if (isdigit(line[i]))
                left.push_back(line[i++]);
            else
                status = OPER;
            break;
        case OPER:
            op = line[i++];
            status = RIGHT;
            break;
        case RIGHT:
            if (isdigit(line[i]))
                right.push_back(line[i++]);
            break;
        }
    }
    Request req;
    req._x = stoi(left);
    req._y = stoi(right);
    req._op = op;
    return req;
}

//./tcpclient serverip serverport

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        userage(argv[0]);
        exit(USAGE_ERR);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    Sock sock;
    sock.Socket();

    int n = sock.Connect(serverip, serverport);
    if (n != 0)
        return 1;

    string buffer;
    while (1)
    {
        cout << "Enter>> ";
        string line;
        getline(cin, line);

        Request req = Parseline(line);

        cout << "test: " << req._x << req._op << req._y << endl;

        // 1.序列化
        string sendString;
        req.Serialize(&sendString);

        // 2.添加报头
        sendString = AddHeader(sendString);

        // 3.发送
        send(sock.Fd(), sendString.c_str(), sendString.size(), 0);

        // 4.获取响应
        string package;
        int n = 0;
    START:
        n = ReadPackage(sock.Fd(), buffer, &package);
        if (n == 0)
            goto START;
        else if (n < 0)
            break;
        else
        {
            // 5.去报头
            package = RemoveHeader(package, n);

            // 6.反序列化
            Response resp;
            resp.Deserialize(package);

            cout << "result: " << resp._result << "[code: " << resp._code << "]" << endl;
        }
    }

    sock.Close();
    return 0;
}

编译运行:

五、JSON

 实际上,对于序列化与反序列化的规则,是不需要程序员自己实现的。因为已经存在了很多更加好用的,别的大佬实现好的规则了。以下是关于JSON的使用。

在Linux中安装JSON:

sudo yum install -y jsoncpp-devel

JSON的头文件会被安装在系统的 /usr/include/ 路径下。动静态库被安装在 /lib64/ 路径下。

在使用JSON时,需要包含头文件:

#include <jsoncpp/json/json.h>

JSON中的部分成员:

  • Value:一种万能对象,能接收任意的kv类型
  • FastWriter:是用来进行序列化的。struct->string,直接把结构体变成一行字符串。
  • StyledWriter:进行风格化的序列化,使字符串更加好看。
  • Reader:用来进行反序列化。
class Request
{
public:
    //...
    bool Serialize(std::string* outStr)
    {

        *outStr = "";
        Json::Value root; //Value:一种万能对象,接受任意的kv类型

        root["x"] = _x; //自动把所有类型转换成字符串
        root["y"] = _y;
        root["op"] = _op;

        Json::FastWriter writer;

        *outStr = writer.write(root);
        return true;
    }

    bool Deserialize(const std::string& inStr)
    {
        Json::Value root;
        Json::Reader reader;

        reader.parse(inStr, root);

        _x = root["x"].asInt();  //把数据从字符串转回指定的类型
        _y = root["y"].asInt();
        _op = root["op"].asInt();
        return true;
    }
public:
    int _x;
    int _y;
    char _op;
};

class Response
{
public:
    //...
    bool Serialize(std::string* outStr)
    {
        *outStr = "";
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        Json::FastWriter writer;
        *outStr = writer.write(root);
        return true;
    }

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

public:
    int _result;
    int _code;
};

 程序正常运行。

六、补充内容

 一个程序里不一定只有一个协议。只需要在定制的报头中包含协议号,使程序能够分辨不同的协议,就可以同时使用多种协议。

 在我们上面定制的报头中,是以 "长度"\r\n"_x _op _y" \r\n 的格式制定的。由于有效载荷中不包含 \r\n ,所以就算不在前面加长度,也能通过搜索字符串的方式读取到有效载荷。但是如果有效载荷中包含分隔符,就会造成错误。所以加上长度是稳妥的做法。

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

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

相关文章

奇思 妙想

一. main方法可以被其它方法调用吗&#xff1f; 在C语言中&#xff0c;一个工程内只能声明一个main函数&#xff0c;如果声明多个&#xff0c;则程序无法运行然后报错。Java则不同&#xff0c;Java在一个工程内&#xff0c;可以声明多个main方法&#xff0c;但在程序执行时&am…

搭建OIDC Provider,以Golang为例

搭建OIDC Provider&#xff0c;以Golang为例 1 需求 结合对OIDC&#xff1a;https://blog.csdn.net/weixin_45747080/article/details/131810562的理解&#xff0c;我尝试自己搭建OIDC的demo。在搭建demo之前&#xff0c;我需要先确定我想要实现成什么样子。以上文提到的http…

【算法|动态规划No.6】leetcode63. 不同路径Ⅱ

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

使用diagrams画架构图

序 最近发现一个画架构图的神器diagrams&#xff0c;提供了很多云厂商及开源组件的图标&#xff0c;相比于C4-PlantUML显得更专业一点。 之前写过技术文档画图工具箱&#xff0c;diagrams属于diagram as code工具派别。 mac安装 brew install graphviz pip install diagrams…

【Matlab】基于遗传算法优化 BP 神经网络的时间序列预测(Excel可直接替换数据)

【Matlab】基于遗传算法优化 BP 神经网络的时间序列预测&#xff08;Excel可直接替换数据&#xff09; 1.模型原理2.文件结构3.Excel数据4.分块代码4.1 arithXover.m4.2 delta.m4.3 ga.m4.4 gabpEval.m4.5 initializega.m4.6 maxGenTerm.m4.7 nonUnifMutation.m4.8 normGeomSel…

【模型压缩】 LPPN论文阅读笔记

LPPN论文阅读笔记 LPPN: A Lightweight Network for Fast Phase Picking 背景 深度学习模型的问题在于计算复杂度较高&#xff0c;在实际数据处理中需要面临较高的处理代价&#xff0c;且需要专用的加速处理设备&#xff0c;如GPU。随着数据累积&#xff0c;迫切需要设计一种…

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)

文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…

17 界面布局--登录界面

要点&#xff1a; 利用widgets做布局&#xff1a;水平&#xff0c;垂直&#xff0c;栅格 利用弹簧设置收缩 widget宽高比实际控件大很多&#xff1a;设置Fixed 如果需要去除其余边框间隙可以设置layout 将最小尺寸和最大尺寸设置为固定即为固定尺寸 设置窗口标题&#xff1a;wi…

基于DeepFace模型设计的人脸识别软件

完整资料进入【数字空间】查看——baidu搜索"writebug" 人脸识别软件(无外部API) V2.0 基于DeepFace模型设计的人脸识别软件 V1.0 基于PCA模型设计的人脸识别软件 V2.0 更新时间&#xff1a;2018-08-15 在观看了吴恩达老师的“深度学习课程”&#xff0c;了解了深…

关于新手学习Ubuntu使用vim,如何使用c/cpp的编译器以及如何使用makefile的详细记录

ubuntu下 首先如何编辑 1.启动vim编辑器 打开终端&#xff0c;输入vim&#xff0c;按回车键。 vim gcc.c 2.进入编辑模式 输入i ,进入插入模式。就可以修改文件内容了。 按“ESC”退出编辑模式。 3.退出 Shift键 “:”&#xff0c;切换到命令模式。 输入“q”后回车&…

基于OpenCV的红绿灯识别

基于OpenCV的红绿灯识别 技术背景 为了实现轻舟航天机器人实现红绿灯的识别&#xff0c;决定采用传统算法OpenCV视觉技术。 技术介绍 航天机器人的红绿灯识别主要基于传统计算机视觉技术&#xff0c;利用OpenCV算法对视频流进行处理&#xff0c;以获取红绿灯的状态信息。具…

【Linux】Tcp服务器的三种与客户端通信方法及守护进程化

全是干货~ 文章目录 前言一、多进程版二、多线程版三、线程池版四、Tcp服务器日志的改进五、将Tcp服务器守护进程化总结 前言 在上一篇文章中&#xff0c;我们实现了Tcp服务器&#xff0c;但是为了演示多进程和多线程的效果&#xff0c;我们将服务器与客户通通信写成了一下死循…

【Linux】 由“进程”过渡到“线程” -- 什么是线程(thread)?

知识引入初识线程1.什么叫做进程&#xff1f;2.什么叫做线程&#xff1f;3.如何看待我们之前学习的进程&#xff1f; 理解线程创建线程函数调用1.线程一旦被创建&#xff0c;几乎所有资源都是被线程所共享的2.与进程之间切换相比&#xff0c;线程的切换 初识线程总结&#xff1…

JWT 的使用

一、简介 JWT将用户的一些信息存储在客户端&#xff0c;访问后台时会带着JWT&#xff0c;服务器要对这个JWT进行检验。 由于signKey是存放在服务器端的&#xff0c;所以比较安全只要JWT被篡改就会立刻发现。 JWT认证的优势 1.简洁&#xff1a;JWT Token数据量小&#xff0c;传…

WebRTC带宽评估 -- Transport-wide Congestion Control

简述&#xff1a;在RTP包中增加transport-wide-cc扩展头&#xff0c;放置传输层面的包序号。视频接收端记录RTP包的接收时间&#xff0c;并通过RTCP Feedback消息反馈到视频发送端&#xff0c;发送端结合缓存的RTP包发送时间&#xff0c;基于丢包和延迟估算当前带宽&#xff0c…

zabbix 企业级监控 (3)Zabbix-server监控mysql及httpd服务

目录 web界面设置 server.zabbix.com 服务器操作 编辑 chk_mysql.sh脚本 查看web效果 web界面设置 1. 2. 3. 4. 5. 6. 7. 8. server.zabbix.com 服务器操作 [rootserver ~]# cd /usr/local/zabbix/etc/ [rootserver etc]# vim zabbix_agentd.conf UnsafeUserParameters1 Us…

Java当中的栈

栈的理解 栈&#xff08;Stack&#xff09;是一种受限的线性数据结构&#xff0c;所谓受限是指栈只暴露栈顶和栈底的操作&#xff0c;其底层是由数组实现的。栈的特性是先进后出。 常用方法 注意上面的peek()方法和pop()方法的区别&#xff01; 实例 import java.util.Stack…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(7 月 19 日论文合集)

文章目录 一、分割|语义相关(12篇)1.1 Disentangle then Parse:Night-time Semantic Segmentation with Illumination Disentanglement1.2 OnlineRefer: A Simple Online Baseline for Referring Video Object Segmentation1.3 MarS3D: A Plug-and-Play Motion-Aware Model for…

LeetCode74.Search-A-2d-Matrix<搜索二维矩阵>

题目&#xff1a; 思路&#xff1a; 矩阵&#xff0c;搜索数是否在矩阵内。那就查找他是否在每一行中。如果符合这一行的范围&#xff0c;那就一直找这一列是否存在&#xff0c;如果存在返回true&#xff1b;否则false&#xff1b; 代码是&#xff1a; //codeclass Solution …

Istio 安全管理 加密证书中心

1 tls认证 2 设置ACL 允许哪些客户端可以访问 哪些客户端不能访问 3 istio里面的认证 加密是可以分为三种类型 对称加密&#xff08;加密和解密用的是同一个密钥&#xff09;非对称加密哈希函数 对称加密 A要发送数据传送给B&#xff0c;那么A要使用一个密钥&#xff0c;里面…