Linux网络 序列化与反序列化

概念

序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。以下是关于序列化与反序列化的介绍:

  • 序列化:将对象的状态信息转换为可以存储或传输的格式,通常是字节序列或文本格式。
  • 反序列化:将序列化后的数据还原为原始对象或数据结构的过程。

出现原因

序列化的出现主要是为了满足在不同系统、不同语言之间进行数据传输和存储的需求,以下是具体原因:

  • 跨平台和跨语言通信:不同的操作系统和编程语言对数据的表示和存储方式各不相同。例如,Java中的对象在内存中的布局和C++中的对象就有很大差异。通过序列化,可以将数据转换为一种通用的格式,如JSON或XML,这样不同平台和语言编写的程序就能够相互理解和处理这些数据,实现跨平台和跨语言的通信。
  • 网络传输:在网络通信中,数据是以字节流的形式传输的。为了能够在网络上传输复杂的数据结构和对象,需要将它们序列化为字节流,然后在接收端进行反序列化,还原为原始的数据结构和对象。
  • 数据持久化:将对象存储到磁盘或数据库中时,需要先将其序列化为字节流或特定的存储格式,以便能够在需要时进行反序列化恢复。

常见的序列化格式

  • JSON:一种轻量级的数据交换格式,易于阅读和编写,广泛应用于Web开发和API设计中。
  • XML:一种标记语言,具有良好的扩展性和可读性,常用于数据存储和配置文件。
  • Protocol Buffers:一种高效的二进制序列化格式,具有较小的存储空间和较快的解析速度,常用于分布式系统和大数据应用中。

JSON

其中,JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,比较常用,以下是关于它的介绍:

    特点

    • 轻量级:JSON比XML更小、更快,更易解析,占用带宽小,适合在网络上传输。
    • 易于阅读和编写:JSON采用类似于C语言家族的习惯,易于人阅读和编写,同时也易于机器解析和生成。
    • 独立于语言:JSON使用JavaScript语法来描述数据对象,但它独立于语言和平台,支持多种编程语言,如C、C++、Java、Python、PHP等。

    数据结构

    • 对象:对象是一个无序的“名称/值”对集合,以 { 左括号开始, } 右括号结束,每个“名称”后跟一个 : 冒号,“名称/值”对之间使用, 逗号分隔。
    • 数组:数组是值的有序集合,以 [ 左中括号开始, ] 右中括号结束,值之间使用, 逗号分隔。
    • :值可以是双引号括起来的字符串、数值、true、false、null、对象或者数组,这些结构可以嵌套。

    例如,有这样的数据

    hello2025.1.18Mike
    

    经过JSON序列化后,变成以下格式

    {"message": "hello","time": "2025.1.18","name": "Mike"
    }
    

    Jsoncpp

    Jsoncpp 是一个用于处理 JSON 数据的 C++ 库。它提供了将 JSON 数据序列化为字符串以及从字符串反序列化为 C++ 数据结构的功能。Jsoncpp 是开源的,广泛用于各种需要处理 JSON 数据的 C++ 项目中。

    安装

    在Linux中,在不同的环境下可以使用对应的指令安装

    C++
    ubuntu:sudo apt-get install libjsoncpp-dev
    Centos: sudo yum install jsoncpp-devel

    安装成功后就会包含在 /usr/include/jsoncpp 中,所以我们在使用该库时需要包含头文件<jsoncpp/json/json.h>.

    序列化

    序列化指的是将数据结构或对象转换为一种格式,以便在网络上传输或存储到文件中。Jsoncpp 提供了多种方式进行序列化:

    使用 Json::Value 的 toStyledString 方法

    优点:将 Json::Value 对象直接转换为格式化的 JSON 字符串。

    示例:
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main()
    {
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    string s = root.toStyledString();
    cout << s << endl;
    return 0;
    }
    
    执行成功后的结果如下:
    {
    "name" : "joe",
    "sex" : "男"
    }

    使用 Json::StreamWriter

    优点:提供了更多的定制选项,如缩进、换行符等。
    示例:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <memory>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main()
    {
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    Json::StreamWriterBuilder wbuilder; // StreamWriter 的工厂
    unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter());
    stringstream ss;
    writer->write(root, &ss);
    cout << ss.str() << endl;
    return 0;
    }
    

    执行成功后的结果如下:

    {
    "name" : "joe",
    "sex" : "男"
    }

    使用 Json::FastWriter

    优点:比 StyledWriter 更快,因为它不添加额外的空格和换行符。
    示例:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <memory>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main()
    {
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    Json::FastWriter writer;
    string s = writer.write(root);
    cout << s << endl;
    return 0;
    }

    执行成功后的结果如下:

    {"name":"joe","sex":"男"}

    反序列化

    反序列化指的是将序列化后的数据重新转换为原来的数据结构或对象。Jsoncpp 提供了以下方法进行反序列化:

    使用 Json::Reader

    优点:提供详细的错误信息和位置,方便调试。
    示例:
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main() {
    // JSON 字符串
    string json_string = "{\"name\":\"张三\",\"age\":30, \"city\":\"北京\"}";
    // 解析 JSON 字符串
    Json::Reader reader;
    Json::Value root;
    // 从字符串中读取 JSON 数据
    bool parsingSuccessful = reader.parse(json_string,root);
    if (!parsingSuccessful) {
    // 解析失败,输出错误信息
    cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << endl;
    return 1;
    }
    // 访问 JSON 数据
    string name = root["name"].asString();
    int age = root["age"].asInt();
    string city = root["city"].asString();
    // 输出结果
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    cout << "City: " << city << endl;
    return 0;
    }

    执行成功后的结果如下:

    Name: 张三
    Age: 30
    City: 北京

    案例 网络版计算器

    例如, 我们需要实现一个服务器版的加法器,我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。

    makefile

    all: server client
    server:TcpServermain.ccg++ -o $@ $^ -std=c++17 -lpthread -ljsoncpp
    client:TcpClient.ccg++ -o $@ $^ -std=c++17 -ljsoncpp
    .PHONY:clean
    clean:rm -f server client

    Mutex.hpp

    #pragma once
    #include <iostream>
    #include <pthread.h>
    using namespace std;class Mutex
    {
    public:Mutex(const Mutex&)=delete;const Mutex& operator=(const Mutex&)=delete;Mutex(){pthread_mutex_init(&_lock,nullptr);}~Mutex(){pthread_mutex_destroy(&_lock);}void Lock(){pthread_mutex_lock(&_lock);}pthread_mutex_t * LockPtr(){return &_lock;}void Unlock(){pthread_mutex_unlock(&_lock);}
    private:pthread_mutex_t _lock;
    };
    class LockGuard
    {public:LockGuard(Mutex& m):_mutex(m){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}private:Mutex& _mutex;
    };

    Cond.hpp

    #pragma once
    #include"Mutex.hpp"
    class Cond
    {public:Cond(){pthread_cond_init(&_cond,nullptr);}~Cond(){pthread_cond_destroy(&_cond);}void Wait(Mutex& mutex){pthread_cond_wait(&_cond,mutex.LockPtr());}void Notify(){pthread_cond_signal(&_cond);}void NotifyAll(){pthread_cond_broadcast(&_cond);}private:pthread_cond_t _cond;
    };

    Thread.hpp

    #pragma once
    #include <pthread.h>
    #include <iostream>
    #include <functional>
    #include <string>
    #include <unistd.h>
    using namespace std;
    using func_t = function<void(string)>;
    static int number = 1;
    enum STATUS
    {NEW,RUNNING,STOP
    };
    class Thread
    {
    private:static void *Routine(void *arg){Thread *t = static_cast<Thread *>(arg);t->_func(t->_name);return nullptr;}public:Thread(func_t func): _func(func), _status(NEW), _joinable(true){_name = "Thread-" + to_string(number++);_pid = getpid();}bool Start(){if (_status != RUNNING){_status = RUNNING;int n = pthread_create(&_tid, nullptr, Routine, this);if (n != 0){return false;}return true;}return false;}bool Stop(){if (_status == RUNNING){_status = STOP;int n = pthread_cancel(_tid);if (n != 0){return false;}return true;}return false;}bool Join(){if (_joinable){_status = STOP;int n = pthread_join(_tid, nullptr);if (n != 0){return false;}return true;}return false;}void Detach(){_joinable = false;pthread_detach(_tid);}string Name(){return _name;}
    private:string _name;pthread_t _tid;pid_t _pid;STATUS _status;bool _joinable;func_t _func;
    };

    ThreadPool.hpp

    #pragma once
    #include <iostream>
    #include <string>
    #include <queue>
    #include <vector>
    #include <memory>
    #include "Mutex.hpp"
    #include "Cond.hpp"
    #include "Thread.hpp"
    using thread_t = shared_ptr<Thread>;
    const static int defaultnum = 5;template <class T>
    class ThreadPool
    {
    private:bool IsEmpty() { return _taskq.empty(); }void HandlerTask(string name){cout << "线程: " << name << ", 进入HandlerTask的逻辑" << endl;while (true){// 1. 拿任务T t;{LockGuard lockguard(_lock);while (IsEmpty() && _isrunning){_wait_num++;_cond.Wait(_lock);_wait_num--;}// 2. 任务队列为空 && 线程池退出了if (IsEmpty() && !_isrunning)break;t = _taskq.front();_taskq.pop();}// 2. 处理任务t(); // 规定,未来所有的任务处理,全部都是必须提供t()方法!}cout << "线程: " << name << " 退出";}ThreadPool(const ThreadPool<T> &) = delete;ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false){for (int i = 0; i < _num; i++){_threads.push_back(make_shared<Thread>(bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));cout << "构建线程" << _threads.back()->Name() << "对象 ... 成功" << endl;}}public:static ThreadPool<T> *getInstance(){if (instance == NULL){LockGuard lockguard(mutex);if (instance == NULL){cout << "单例首次被执行,需要加载对象..." << endl;instance = new ThreadPool<T>();instance->Start();}}return instance;}void Equeue(T in){LockGuard lockguard(_lock);if (!_isrunning)return;_taskq.push(in);if (_wait_num > 0)_cond.Notify();}void Start(){if (_isrunning)return;_isrunning = true;for (auto &thread_ptr : _threads){cout << "启动线程" << thread_ptr->Name() << " ... 成功";thread_ptr->Start();}}void Wait(){for (auto &thread_ptr : _threads){thread_ptr->Join();cout << "回收线程" << thread_ptr->Name() << " ... 成功";}}void Stop(){LockGuard lockguard(_lock);if (_isrunning){_isrunning = false; // 不工作// 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了if (_wait_num > 0)_cond.NotifyAll();}}private:vector<thread_t> _threads;int _num;int _wait_num;std::queue<T> _taskq; // 临界资源Mutex _lock;Cond _cond;bool _isrunning;static ThreadPool<T> *instance;static Mutex mutex; // 只用来保护单例
    };template <class T>
    ThreadPool<T> *ThreadPool<T>::instance = NULL;
    template <class T>
    Mutex ThreadPool<T>::mutex; // 只用来保护单例

    InetAddr.hpp

    #pragma once
    #include <string>
    #include <iostream>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <cstring>
    using namespace std;
    class InetAddr
    {
    public:InetAddr();InetAddr(int port, string ip = ""): _port(port), _ip(ip){bzero(&_sockaddr, sizeof(_sockaddr));_sockaddr.sin_family = AF_INET;_sockaddr.sin_port = htons(_port);if (_ip.empty())_sockaddr.sin_addr.s_addr = INADDR_ANY;else_sockaddr.sin_addr.s_addr = inet_addr(_ip.c_str());}InetAddr(const struct sockaddr_in &sockaddr){_port = ntohs(sockaddr.sin_port);char buf[64];_ip = inet_ntop(AF_INET, &sockaddr.sin_addr, buf, sizeof(buf));}bool operator==(const InetAddr &other){return _ip == other._ip;}InetAddr operator=(const InetAddr &other){_ip = other._ip;_port = other._port;_sockaddr = other._sockaddr;return *this;}struct sockaddr *getSockaddr(){return (struct sockaddr *)&_sockaddr;}int getSockaddrLen(){return sizeof(_sockaddr);}const string &getIp(){return _ip;}int getPort(){return _port;}private:string _ip;int _port;struct sockaddr_in _sockaddr;
    };

    Common.hpp

    enum
    {SOCKET_ERROR=1,BIND_ERROR,LISTEN_ERROR,ACCEPT_ERROR,CONNECT_ERROR
    };

    Protocol.hpp

    #pragma once
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
    using namespace std;const string Sep = "\r\n";
    // 给信息添加报头
    //{json} -> length\r\n{json}\r\n
    bool Encode(string &message)
    {if (message.size() == 0)return false;string package = to_string(message.size()) + Sep + message + Sep;message = package;return true;
    }
    // 解析协议,提取信息
    bool Decode(string &package, string *message)
    {auto pos = package.find(Sep);if (pos == string::npos)                                                                                                              return false;string message_length_str = package.substr(0, pos);int message_length = stoi(message_length_str);int full_length = message_length_str.size() + 2 * Sep.size() + message_length;if (package.size() < full_length)return false;*message = package.substr(pos + Sep.size(), message_length);package.erase(0,full_length);return true;
    }
    class Request
    {
    public:Request(){}Request(int x, int y, char op): _x(x), _y(y), _op(op){}// 使用jsoncpp序列化void Serialize(string &out_str){Json::Value root;root["x"] = _x;root["y"] = _y;root["op"] = _op;out_str = root.toStyledString();}// 反序列化bool Deserialize(string &in_str){Json::Value root;Json::Reader reader;bool parsingSuccessful = reader.parse(in_str, root);if (!parsingSuccessful){cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages();return false;}_x = root["x"].asInt();_y = root["y"].asInt();_op = root["op"].asInt();return true;}void Print(){cout<<"x: "<<_x << endl;cout<<"y: "<<_y << endl;cout<<"op: "<<_op << endl;}int X() const {return _x;}int Y() const {return _y;}char Op() const {return _op;}
    private:int _x, _y;char _op;
    };class Response
    {public:Response(){}Response(int result ,int code):_result(result),_code(code){}void Serialize(string& out_str){Json::Value root;root["result"]=_result;root["code"]=_code;out_str=root.toStyledString();}bool Deserialize(string& in_str){Json::Value root;Json::Reader reader;bool parsingsuccessful=reader.parse(in_str,root);if(!parsingsuccessful){cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << endl;return false;}_result = root["result"].asInt();_code = root["code"].asInt();return true;}void SetResult(int res){_result=res;}void SetCode(int c){_code=c;}int Result(){return _result;}int Code(){return _code;}private:int _result = 0;int _code = 0;
    };

    TcpServer.hpp

    #pragma once
    #include <iostream>
    #include <pthread.h>
    #include <functional>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <cstring>
    #include <memory>
    #include "Common.hpp"
    #include "InetAddr.hpp"
    #include "ThreadPool.hpp"
    using namespace std;#define BACKLOG 8
    using handler_t = function<string(string &)>;
    static const uint16_t gport = 8080;class TcpServer
    {using task_t = function<void()>;struct ThreadData{int sockfd;TcpServer *self;};public:TcpServer(handler_t handler, uint16_t port = gport): _handler(handler), _port(port), _isrunning(false){}void InitServer(){// 创建socket_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){cout << "socket error" << endl;exit(SOCKET_ERROR);}cout << "socket create success,sockfd is: " << _listensockfd << endl;// 填写IP端口struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;// bindint ret = bind(_listensockfd, (struct sockaddr *)&local, sizeof(local));if (ret < 0){cout << "bind error" << endl;exit(BIND_ERROR);}cout << "bind success" << endl;// 将socket设置为监听状态ret = listen(_listensockfd, BACKLOG);if (ret < 0){cout << "listen error" << endl;exit(LISTEN_ERROR);}cout << "listen success" << endl;}void HandleRequest(int sockfd) // TCP为全双工通信{char buffer[4096];string package;while (true){// int n = read(sockfd, buffer, sizeof(buffer) - 1);int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;cout << buffer << endl;package += buffer;string cmd_result = _handler(package);// write(sockfd, cmd_result.c_str(), cmd_result.size());if (cmd_result.empty())continue;cout << cmd_result<<endl;send(sockfd, cmd_result.c_str(), cmd_result.size(), 0);}else if (n == 0){// 如果读取的值为0,说明client退出cout << "client quit" << endl;break;}else{// 读取失败break;}}close(sockfd);}void Start(){_isrunning = true;while (_isrunning){// 获取新连接struct sockaddr_in peer;socklen_t peerlen = sizeof(peer);int sockfd = accept(_listensockfd, (struct sockaddr *)&peer, &peerlen);if (sockfd < 0){cout << "accept error" << endl;exit(ACCEPT_ERROR);}cout << "accept success,sockfd is: " << sockfd << endl;InetAddr addr(peer);cout << "client info: " << addr.getIp() << ":" << addr.getPort() << endl;// 将任务交给线程池ThreadPool<task_t>::getInstance()->Equeue([&](){this->HandleRequest(sockfd);});}}void Stop(){_isrunning = false;}private:int _listensockfd; // 监听socketuint16_t _port;bool _isrunning;// 处理上层任务的入口handler_t _handler;
    };

    TcpServermain.cc

    #include "TcpServer.hpp"
    #include "CommandExec.hpp"
    #include "Daemon.hpp"
    #include "Calculator.hpp"
    #include <functional>
    #include <unistd.h>
    #include <memory>
    // 解析package
    using cal_func = function<Response(const Request &)>;
    class Parse
    {
    public:Parse(cal_func func): _func(func){}// 提取报文中一次计算的完整信息string Entry(string &package){// 判断报文完整性string message;string resstr;while (Decode(package, &message)){cout << message;if (message.empty())break;// 反序列化Request req;if (!req.Deserialize(message))break;cout << "Request: ";req.Print();// 计算Response res = _func(req);// 序列化string tmp;res.Serialize(tmp);cout << "序列化: " << tmp << endl;// 添加报头Encode(tmp);cout << "Encode: " << tmp;// 拼接应答resstr += tmp;}return resstr;}private:cal_func _func;
    };
    int main()
    {// 变成守护进程Daemon(false, false);Calculator mycal;Parse mypar([&](const Request &req){ return mycal.Execute(req); });unique_ptr<TcpServer> server = make_unique<TcpServer>([&](string& package){ return mypar.Entry(package); });server->InitServer();server->Start();return 0;
    }

    TcpClient.cc

    #include <iostream>
    #include <string>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <cstring>
    using namespace std;
    #include "Common.hpp"
    #include "Protocol.hpp"
    //./client server_ip server_port
    int main(int argc, char *argv[])
    {if (argc != 3){cout << "Usage:./client server_ip server_port" << endl;return 0;}string server_ip = argv[1];int server_port = stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cout << "socket create error" << endl;exit(SOCKET_ERROR);}// 填写网络信息struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(server_port);server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());// client 无需显示bind,connect连接时自动bindint n = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (n < 0){cout << "connect error" << endl;exit(CONNECT_ERROR);}string message;while (true){int x, y;char op;cout << "input x: ";cin >> x;cout << "input y: ";cin >> y;cout << "input op: ";cin >> op;Request req(x, y, op);req.Serialize(message); // 序列化Encode(message);        // 添加协议n = send(sockfd, message.c_str(), message.size(), 0);if (n > 0){char buffer[1024];int m = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (m > 0){buffer[m] = 0;string package = buffer;string content;Decode(package, &content); // 去报头提取内容Response res;              // 反序列化res.Deserialize(content);cout << res.Result() << "[" << res.Code() << "]" << endl;}elsebreak;}elsebreak;}close(sockfd);return 0;
    }

     

       

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

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

      相关文章

      使用 spring boot 2.5.6 版本时缺少 jvm 配置项

      2.5.6我正在使用带有版本和springfox-boot-starter版本的Spring Boot 项目3.0.0。我的项目还包括一个WebSecurityConfig扩展WebSecurityConfigurerAdapter并实现WebMvcConfigurer的类。但是&#xff0c;我面临的问题是指标在端点jvm_memory_usage_after_gc_percent中不可见/act…

      python在财务领域的应用

      财务岗位在处理数据时&#xff0c;经常会遇到一些复杂的场景&#xff0c;Excel 虽然功能强大&#xff0c;但在某些情况下可能无法高效或灵活地解决问题。以下是一些常见的、需要用编程&#xff08;如 Python、R 或 SQL&#xff09;来解决的数据问题&#xff1a; 1. 大规模数据处…

      ZooKeeper 中的 ZAB 一致性协议与 Zookeeper 设计目的、使用场景、相关概念(数据模型、myid、事务 ID、版本、监听器、ACL、角色)

      参考Zookeeper 介绍——设计目的、使用场景、相关概念&#xff08;数据模型、myid、事务 ID、版本、监听器、ACL、角色&#xff09; ZooKeeper 设计目的、特性、使用场景 ZooKeeper 的四个设计目标ZooKeeper 可以保证如下分布式一致性特性ZooKeeper 是一个典型的分布式数据一致…

      Objective-C语言的数据类型

      Objective-C数据类型详解 Objective-C是一种面向对象的编程语言&#xff0c;主要用于macOS和iOS应用程序的开发。作为C语言的超集&#xff0c;Objective-C继承了C语言的基本数据类型&#xff0c;同时也引入了一些独特的特性。本文将对Objective-C的各种数据类型进行详细的介绍…

      Spring Boot自动配置原理:如何实现零配置启动

      引言 在现代软件开发中&#xff0c;Spring 框架已经成为 Java 开发领域不可或缺的一部分。而 Spring Boot 的出现&#xff0c;更是为 Spring 应用的开发带来了革命性的变化。Spring Boot 的核心优势之一就是它的“自动配置”能力&#xff0c;它极大地简化了 Spring 应用的配置…

      大模型GUI系列论文阅读 DAY2续2:《使用指令微调基础模型的多模态网页导航》

      摘要 自主网页导航的进展一直受到以下因素的阻碍&#xff1a; 依赖于数十亿次的探索性交互&#xff08;通常采用在线强化学习&#xff09;&#xff0c;依赖于特定领域的模型设计&#xff0c;难以利用丰富的跨领域数据进行泛化。 在本研究中&#xff0c;我们探讨了基于视觉-语…

      在视频汇聚平台EasyNVR平台中使用RTSP拉流的具体步骤

      之前有用户反馈&#xff0c;在EasyNVR平台中添加Pull时使用海康设备的RTSP流地址无法播放。经过研发的优化及一系列严谨的验证流程&#xff0c;我们已确认优化后的EasyNVR平台&#xff0c;通过Pull方式添加海康设备的RTSP流已经能够正常播放。以下是具体的操作步骤&#xff1a;…

      Debezium日常分享系列之:对于从Oracle数据库进行快照的性能优化

      Debezium日常分享系列之&#xff1a;对于从Oracle数据库进行快照的性能优化 源数据库Kafka Connect监控测试结果 源数据库 Oracle 19c&#xff0c;本地&#xff0c;CDB数据库主机的I/O带宽为6 GB/s&#xff0c;由此主机上运行的所有数据库共享临时表空间由42个文件组成&#x…

      C++书籍 第一部分专业C++程序设计概述

      1&#xff0c;必不可少的“hello world” #include<iostream>int main(int argc, char** argv) {std::cout << "hello world" << std::endl;return 0; } 这个是一个极其简单的程序&#xff0c;虽然没有多大简直&#xff0c;但是可以体现c程序格式方…

      VIVADO ILA IP进阶使用之任意设置ILA的采样频率

      VIVADO ILA IP进阶使用之任意设置ILA的采样频率 VIVADO ILA IP和VIO IP结合使用任意设置ILA的采样频率 目录 前言 一、VIO IP的配置 二、ILA IP的配置 三、测试代码 四、测试结果 总结 前言 VIVADO中编写完程序上板测试时经常会用到viavdo自带的ILA逻辑分析仪IP核&#x…

      spring @EnableAspectJAutoProxy @Aspect的使用和源码流程

      目录 测试代码EnableAspectJAutoProxyAspectJAutoProxyRegistrarAnnotationAwareAspectJAutoProxyCreatororg.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors 实例化AnnotationAwareAspectJAutoProxyCreator bean "a"的代理…

      Mono里运行C#脚本29—mono_trampolines_init

      一、概念解释 在计算机编程中,trampoline 通常是一段代码,它起到一个中间跳转的作用。它就像一个跳板,程序可以先跳转到这个跳板上,然后再从跳板跳转到最终的目的地。这种技术在许多不同的场景中都有应用,以下是一些主要方面: 函数调用方面: 当涉及到不同执行环境或不…

      【BUUCTF】[GXYCTF2019]BabySQli

      进入页面如下 尝试万能密码注入 显示这个&#xff08;qyq&#xff09; 用burp suite抓包试试 发现注释处是某种编码像是base编码格式 MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5 可以使用下面这个网页在线工具很方便…

      重生之我在异世界学编程之算法与数据结构:深入堆篇

      大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 正文一、堆的基本概念二、堆的存储表示三…

      《自动驾驶与机器人中的SLAM技术》ch8:基于预积分和图优化的紧耦合 LIO 系统

      目录 1 预积分 LIO 系统的经验 2 预积分图优化的顶点 3 预积分图优化的边 3.1 NDT 残差边&#xff08;观测值维度为 3 维的单元边&#xff09; 4 基于预积分和图优化 LIO 系统的实现 4.1 IMU 静止初始化 4.2 使用预积分预测 4.3 使用 IMU 预测位姿进行运动补偿 4.4 位姿配准部…

      软件测试—— 接口测试(HTTP和HTTPS)

      软件测试—— 接口测试&#xff08;HTTP和HTTPS&#xff09; HTTP请求方法GET特点使用场景URL结构URL组成部分URL编码总结 POST特点使用场景请求结构示例 请求标头和响应标头请求标头&#xff08;Request Headers&#xff09;示例请求标头 响应标头&#xff08;Response Header…

      【Excel超实用,VLOOKUP函数,通过excel数据精准匹配,将一个excel文件的某列数据,用另一个excel文件快速填充】

      1、使用背景 如下图1所示&#xff0c;1.xlsx文件&#xff0c;有两列数据&#xff0c;一列序号&#xff0c;一列内容&#xff0c; 我现在需要将第二列的内容快速完成填充&#xff0c;并且有相应的excel模板作为参照。 图1 如图2所示&#xff0c;2.xlsx是模板文件&#xff0c;序…

      FastExcel 新一代的潮流 (EasyExcel)

      目录 简介 FastExcel的特点 FastExcel使用方法详解 创建实体类和监听器 实现写入和读取功能 Excel转换为PDF 小结 FastExcel与EasyExcel的区别 结论 简介 FastExcel是由原EasyExcel作者在阿里巴巴宣布停止维护EasyExcel之后推出的升级版框架。它继承了EasyExcel的所有…

      Transformer详解:Attention机制原理

      前言 Hello&#xff0c;大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者&#xff0c;本系列文章是作者参加DataWhale2025年1月份学习赛&#xff0c;旨在讲解Transformer模型的理论和实践。&#x1f632; 本文将详细探讨Attention机制的原理…

      Android 11适配全攻略:从理论到实践

      随着Google正式发布Android 11&#xff0c;开发者们迎来了新的挑战和机遇。Android 11不仅带来了全新的用户体验和功能提升&#xff0c;还要求开发者们对应用进行相应的适配&#xff0c;以确保应用的兼容性和稳定性。本文将从理论到实践&#xff0c;全面解析Android 11的适配攻…