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.rhkb.cn/news/5973.html

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

      相关文章

      【前端】CSS实战之音乐播放器

      目录 播放器背景旋转音乐封面按钮进度条音量调节音乐信息按钮的效果JavaScript部分播放和暂停音乐切换音乐信息进度条 音量调节避免拖拽时的杂音音量调节条静音和解除静音 自动下一首实现一个小效果最终效果 播放器背景 <div class"play_box"></div>设置…

      SSM开发(一)JAVA,javaEE,spring,springmvc,springboot,SSM,SSH等几个概念区别

      目录 JAVA 框架 javaEE spring springmvc springboot SSM SSH maven JAVA 一种面向对象、高级编程语言&#xff0c;Python也是高级编程语言&#xff1b;不是框架(框架&#xff1a;一般用于大型复杂需求项目&#xff0c;用于快速开发)具有三大特性&#xff0c;所谓Jav…

      rocketmq基本架构

      简介 Name server 负责broker注册、心跳&#xff0c;路由等功能&#xff0c;类似Kafka的ZKname server节点之间不互相通信&#xff0c;broker需要和所有name server进行通信。扩容name server需要重启broker&#xff0c;不然broker不会和name server建立连接producer和consum…

      【Web】2025-SUCTF个人wp

      目录 SU_blog SU_photogallery SU_POP SU_blog 先是注册功能覆盖admin账号 以admin身份登录&#xff0c;拿到读文件的权限 ./article?filearticles/..././..././..././..././..././..././etc/passwd ./article?filearticles/..././..././..././..././..././..././proc/1…

      【优选算法】6----查找总价格为目标值的两个商品

      这道题相对于前寄到算法题较为容易~ 同样也是使用了双指针的算法哦~ ----------------------------------------begin-------------------------------------- 题目解析&#xff1a; 题目也是很简单地一句话&#xff0c;但是意图还是很明确~ 讲解算法原理&#xff1a; 同样的&…

      github登录用的TOTP和恢复码都丢失了怎么办

      从22年左右开始github的登录就需要用TOTP的一个6位秘钥做二次认证登录&#xff0c;如果在用的TOTP软件失效了&#xff0c;可以用github开启二次认证时下载的恢复码重置认证&#xff0c;但是如果你和我一样这两个东西都没了就只能用邮箱重置了&#xff0c;过程给大家分享一下 一…

      FFmpeg常用命令

      文章目录 一、 FFmpeg 音视频的处理流程二、FFmpeg 常用命令2.1、查看本机支持的采集设备2.2、 录制视频2.2.1、原始视频2.2.2、编码的视频 2.3、录制音频&#xff1a;2.3.1、原始音频2.3.2、编码的音频 2.4、录制音视频&#xff1a;2.5、文件格式转换&#xff1a;2.6、提取音频…

      30天开发操作系统 第 17 天 -- 命令行窗口

      前言 今天一开始&#xff0c;请大家先回忆一下任务A的情形。在harib13e中&#xff0c;任务A下面的LEVEL中有任务因此FIFO为空时我们可以让任务A进入休眠状态。那么&#xff0c;如果我们并未启动任务B0~ B0~ B2, B2的话&#xff0c;任务A又将会如何呢&#xff1f; 首先&#xf…

      OpenEuler学习笔记(九):安装 OpenEuler后配置和优化

      安装OpenEuler后&#xff0c;可以从系统基础设置、网络配置、性能优化等方面进行配置和优化&#xff0c;以下是具体内容&#xff1a; 系统基础设置 更新系统&#xff1a;以root用户登录系统后&#xff0c;在终端中执行sudo yum update命令&#xff0c;对系统进行更新&#x…

      网络安全 | 入侵检测系统(IDS)与入侵防御系统(IPS):如何识别并阻止威胁

      网络安全 | 入侵检测系统&#xff08;IDS&#xff09;与入侵防御系统&#xff08;IPS&#xff09;&#xff1a;如何识别并阻止威胁 一、前言二、入侵检测系统&#xff08;IDS&#xff09;2.1 IDS 的工作原理2.2 IDS 的技术类型2.3 IDS 的部署方式 三、入侵防御系统&#xff08;…

      数学规划问题2 .有代码(非线性规划模型,最大最小化模型,多目标规划模型)

      非线性规划模型 FIrst:转化为标准型 在matlab中求非线性规划的函数 练习题: 典型例题: 最大最小化模型 核心思想&#xff1a; matlab的模型求解 经典例题: 多目标规划模型 基本概念 求解思路: 模型构建步骤 经典例题: 非线性规划模型 非线性规划&#xff08;Nonl…

      2025年最新深度学习环境搭建:Win11+ cuDNN + CUDA + Pytorch +深度学习环境配置保姆级教程

      本文目录 一、查看驱动版本1.1 查看显卡驱动1.2 显卡驱动和CUDA对应版本1.3 Pytorch和Python对应的版本1.4 Pytorch和CUDA对应的版本 二、安装CUDA三、安装cuDANN四、安装pytorch五、验证是否安装成功 一、查看驱动版本 1.1 查看显卡驱动 输入命令nvidia-smi可以查看对应的驱…

      Transformer详解:Attention机制原理

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

      npm install 报错:Command failed: git checkout 2.2.0-c

      [TOC](npm install 报错&#xff1a;Command failed: git checkout 2.2.0-c) npm install 报错&#xff1a;Command failed: git checkout 2.2.0-c export NODE_HOME/usr/local/node-v14.14.0-linux-x64 npm config set registry https://registry.npmmirror.com 使用如上环…

      从对等通信到万维网:通信模型变迁与拥塞求解

      Leonard Kleinrock&#xff1a;我很清楚用不了多久这些计算机就会有相互通信的需求&#xff0c;但如何协调处理这些分时系统概率性产生的分组(不同于电路交换)&#xff0c;却没有有效的方法&#xff0c;我有处理该问题的方法&#xff0c;因此对于我的博士研究&#xff0c;我决定…

      【Vim Masterclass 笔记21】S09L39:Vim 设置与 vimrc 文件的用法示例(二)

      文章目录 S09L39 Vim Settings and the Vimrc File - Part 21 Vim 的配色方案与 color 命令2 map 命令3 示例&#xff1a;用 map 命令快速生成 HTML 代码片段4 Vim 中的 Leader 键5 用 mkvimrc 命令自动生成配置文件 写在前面 本篇为 Vim 自定义配置的第二部分。当中的每个知识…

      Debian 上安装PHP

      1、安装软件源拓展工具 apt -y install software-properties-common apt-transport-https lsb-release ca-certificates 2、添加 Ondřej Sur 的 PHP PPA 源&#xff0c;需要按一次回车&#xff1a; add-apt-repository ppa:ondrej/php 3、更新软件源缓存&#xff1a; apt-g…

      docker Ubuntu实战

      目录 Ubuntu系统环境说明 一、如何安装docker 二、发布.netcore应用到docker中 Ubuntu系统环境说明 cat /etc/os-release PRETTY_NAME"Ubuntu 22.04.5 LTS" NAME"Ubuntu" VERSION_ID"22.04" VERSION"22.04.5 LTS (Jammy Jellyfish)&quo…

      Android OpenGL(六) 纹理

      纹理 纹理是一个2D图片&#xff08;甚至也有1D和3D的纹理&#xff09;&#xff0c; 它可以用来添加物体的细节&#xff1b;你可以想象纹理是一张绘有砖块的纸&#xff0c;无缝折叠贴合到你的3D的 房子上&#xff0c;这样你的房子看起来就像有砖墙外表了 纹理环绕方式 纹理坐…

      C# 网络协议第三方库Protobuf的使用

      为什么要使用二进制数据 通常我们写一个简单的网络通讯软件可能使用的最多的是字符串类型&#xff0c;比较简单&#xff0c;例如发送格式为(head)19|Msg:Heart|100,x,y,z…&#xff0c;在接收端会解析收到的socket数据。 这样通常是完全可行的&#xff0c;但是随着数据量变大&…