自定义协议:序列化与反序列化的深度解析与实践

news2024/11/23 3:24:44

小白苦学IT的博客主页⭐

初学者必看:Linux操作系统入门⭐

代码仓库:Linux代码仓库⭐

❤关注我一起讨论和学习Linux系统

1.引言

协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?

在数据通信和程序交互的世界里,自定义协议是沟通的桥梁,而序列化和反序列化则是这座桥梁上的重要交通枢纽。它们将复杂的数据结构转化为可传输和可存储的格式,为数据在不同系统间的流通提供了可能。本文将深入探讨序列化和反序列化的概念,并通过一个实际案例来加深理解,最后给出具体的代码实现。

2.概念解析

2.1自定义协议

自定义协议,顾名思义,就是根据特定需求定制的通信协议。在数据交互频繁的现代应用中,我们往往需要定义一套或多套协议来规范数据的格式、传输方式和处理逻辑。这些协议可以是简单的文本协议,也可以是复杂的二进制协议,关键在于它们能够满足我们的实际需求。

为什么我们需要自定义协议呢?

  • 满足特定需求:不同的应用或系统对数据的处理方式和传输要求可能各不相同。通过自定义协议,我们可以根据实际需求来定义数据的格式和传输方式,以满足特定的业务需求。
  • 优化性能:自定义协议可以根据网络条件、数据类型和传输需求等因素进行优化,以提高数据传输的效率和稳定性。
  • 保障安全性:通过自定义协议,我们可以加入数据加密、身份验证等安全措施,以保障数据在传输过程中的安全性和完整性。

2.2序列化和反序列化

序列化

序列化是指将数据结构或对象状态转换为可以存储或传输的格式的过程。这个过程会将数据结构或对象转换为一个连续的字节序列,即所谓的序列化形式。这样,数据就可以被写入持久存储,如硬盘,或者通过网络进行传输。

序列化的主要目的有以下几点:

数据传输:通过网络将数据结构发送到其他系统或应用程序,实现跨平台、跨语言的数据交换。

数据持久化:将数据结构保存到文件或数据库中,以便在需要时重新加载和恢复数据状态。

常见的序列化方法包括:

  1. JSON序列化:将数据结构转换为JSON格式的字符串,易于阅读和解析。
  2. Protocol Buffers(protobuf)序列化:由Google开发的一种二进制数据序列化格式,具有高效的编码和解码性能。
  3. XML序列化:将数据结构转换为XML格式的字符串,适用于需要跨平台交互的场景。

反序列化

反序列化是序列化的逆过程。它读取序列化后的字节序列,并将其恢复为原始的数据结构或对象状态。反序列化的过程需要遵循与序列化相同的协议和规则,以确保数据的正确性和完整性。

反序列化的作用主要有以下几点:

  1. 数据还原:将接收到的序列化数据还原为原始的数据结构,以便在本地系统或应用程序中进行处理。
  2. 对象重建:根据序列化数据中保存的对象状态,重新构建出完整的对象实例。

自定义协议的重要性,以及序列化和反序列化在通信协议中的核心作用。

自定义协议的重要性体现在多个方面。首先,自定义协议能够根据特定应用的需求,精确定义数据的格式、传输方式和处理逻辑,从而确保数据在不同系统间的准确流通。这对于提升系统间的交互效率、保障数据的一致性和安全性至关重要。其次,自定义协议还可以增强系统的灵活性和扩展性,使得系统能够应对未来可能出现的新需求和新挑战。

序列化和反序列化在通信协议中扮演着核心的角色。序列化是将数据结构或对象状态转换为可存储或传输的格式的过程,而反序列化则是将序列化后的数据恢复为原始数据结构或对象状态的过程。这两个过程在数据通信中起着桥梁的作用,使得数据能够在不同系统间进行无障碍的传输和交换。

具体来说,序列化使得数据能够被写入持久存储或通过网络发送,从而实现了数据的跨系统流通。而反序列化则确保了接收方能够正确解析和处理接收到的数据,还原出原始的数据结构或对象状态,进而进行相应的业务逻辑处理。

因此,序列化和反序列化是通信协议中不可或缺的一部分,它们保障了数据的正确性和完整性,提升了系统间的交互效率和可靠性。在实际应用中,我们需要根据具体需求来设计和实现自定义协议中的序列化和反序列化过程,以确保数据在不同系统间的准确、高效流通。

反序列化与序列化是紧密相关的,它们共同构成了数据通信和持久化的基础。通过序列化和反序列化,我们可以实现数据的跨平台、跨语言传输和交换,以及数据的持久化保存和恢复。

2.3用一个故事来理解以上概念

故事时刻

在《王者荣耀》这款游戏中,我们可以借助自定义协议、序列化与反序列化的概念来更好地理解其背后的数据通信和交换机制。

首先,我们来看自定义协议。在《王者荣耀》中,游戏开发者根据游戏的特性和需求,定义了一套独特的通信协议。这套协议规定了游戏中各种数据(如玩家信息、游戏状态、操作指令等)的格式、传输方式和处理逻辑。正是这套自定义协议,确保了游戏内数据的准确、高效流通,为玩家提供了流畅的游戏体验。

接下来,我们来看序列化。在《王者荣耀》中,当玩家进行游戏时,他们的操作、状态以及游戏内的各种事件都会产生大量的数据。这些数据需要被转换为一种可传输的格式,以便在网络上进行传输。这就是序列化的过程。例如,玩家的移动指令、技能释放等操作,都会被游戏系统序列化为特定的字节序列,然后通过网络发送给服务器。

服务器在接收到这些序列化后的数据后,需要进行反序列化的过程。反序列化是将接收到的字节序列还原为原始的数据结构或对象状态的过程。在《王者荣耀》中,服务器会根据自定义协议中规定的格式和逻辑,将接收到的字节序列反序列化为具体的操作指令或游戏事件。这样,服务器就能准确地理解玩家的操作意图,并据此更新游戏状态。

通过序列化和反序列化的过程,游戏中的数据能够在玩家和服务器之间进行无障碍的传输和交换。这使得玩家能够实时地与服务器进行交互,获得及时的游戏反馈和体验。

总的来说,自定义协议、序列化和反序列化在《王者荣耀》这款游戏中发挥着重要的作用。它们确保了游戏内数据的准确、高效流通,为玩家提供了流畅、稳定的游戏体验。

3.网络版计算器(完整代码在gitee仓库中)

自定义协议代码(序列化和反序列化)

Protocol.hpp

#pragma once

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>

const std::string blank_space_sep = " ";
const std::string protocol_sep = "\n";

std::string Encode(std::string &content)
{
    std::string package = std::to_string(content.size());
    package += protocol_sep;
    package += content;
    package += protocol_sep;

    return package;
}

bool Decode(std::string &package, std::string *content)
{
    std::size_t pos = package.find(protocol_sep);
    if (pos == std::string::npos)
        return false;
    std::string str_len = package.substr(0, pos);
    std::size_t len = std::stoi(str_len);
    std::size_t total_len = str_len.size() + len + 2;
    if (package.size() < total_len)
        return false;
    *content = package.substr(pos + 1, len);

    package.erase(0, total_len);
    return true;
}

class Request
{
public:
    Request(int data1, int data2, char oper) : x(data1), y(data2), op(oper)
    {
    }

    Request()
    {
    }

    ~Request()
    {
    }

public:
    bool Serialize(std::string *out)
    {
#ifdef MySelf
        // 构建报文的有效载荷
        std::string s = std::to_string(x);
        s += blank_space_sep;
        s += op;
        s += blank_space_sep;
        s += std::to_string(y);

        // 封装报文
        *out = s;
        return true;
#else
        Json::Value root;
        root["x"] = x;
        root["y"] = y;
        root["op"] = op;
        Json::FastWriter w;
        *out = w.write(root);
        return true;

#endif
    }

    bool Deserialize(const std::string &in)
    {
#ifdef MySelf
        std::size_t left = in.find(blank_space_sep);
        if (left == std::string::npos)
            return false;
        std::string part_x = in.substr(0, left);

        std::size_t right = in.rfind(blank_space_sep);
        if (right == std::string::npos)
            return false;
        std::string part_y = in.substr(right + 1);

        if (left + 2 != right)
            return false;
        op = in[left + 1];
        x = std::stoi(part_x);
        y = std::stoi(part_y);
        return true;
#else
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);

        x = root["x"].asInt();
        y = root["y"].asInt();
        op = root["op"].asInt();
        return true;


#endif
    }

    void DebugPrint()
    {
        std::cout << "请求构建完成: " << x << op << y << "=?" << std::endl;
    }

public:
    int x;
    int y;
    char op;
};

class Response
{
public:
    Response()
    {
    }

    Response(int res, int c) : result(res), code(c)
    {
    }

public:
    bool Serialize(std::string *out)
    {
#ifdef MySelf
        // 构建报文的有效载荷
        std::string s = std::to_string(result);
        s += blank_space_sep;
        s += std::to_string(code);

        // 封装报文
        *out = s;
        return true;
#else
        Json::Value root;
        root["result"] = result;
        root["code"] = code;
        Json::FastWriter w;
        *out = w.write(root);
        return true;

#endif
    }

    bool Deserialize(const std::string &in)
    {
#ifdef MySelf
        std::size_t pos = in.find(blank_space_sep);
        if (pos == std::string::npos)
        {
            return false;
        }

        std::string left = in.substr(0, pos);
        std::string right = in.substr(pos + 1);

        result = std::stoi(left);
        code = std::stoi(right);
        return true;
#else
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);

        result = root["result"].asInt();
        code = root["code"].asInt();
        return true;
#endif
    }

    void DebugPrint()
    {
        std::cout << "结果响应完成: result: " << result << ", code: " << code << std::endl;
    }

public:
    int result;
    int code;
};

ServerCal.hpp

#pragma once

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

enum
{
    Div_ZERO = 1,
    Model_ZERO,
    Other_Oper
};

class ServerCal
{
public:
    ServerCal(){}

    Response CalculatorHelper(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 = Div_ZERO;
                else resp.result = req.x / req.y;
            }
            break;
        case '%':
            {
                if(req.y == 0)resp.code = Model_ZERO;
                else resp.result = req.x % req.y;
            }
            break;
        default:
            resp.code = Other_Oper;
            break;
        }
        return resp;
    }

    std::string Calculator(std::string & package)
    {
        std::string content;
        bool r = Decode(package,&content);
        if(!r) return "";
        Request req;
        r = req.Deserialize(content);
        if(!r) return "";

        content = "";

        Response resp = CalculatorHelper(req);
        resp.Serialize(&content);
        content = Encode(content);
        return content;
    }

    ~ServerCal(){}
};

TcpServer.hpp

#pragma once
#include"Log.hpp"
#include"Socket.hpp"
#include<signal.h>
#include<functional>

using func_t = std::function<std::string (std::string & package)>;


class TcpServer
{
public:
    TcpServer(){}

     TcpServer(const uint16_t & port,func_t fun):_port(port),callback_(fun)
     {}
    bool ServerInit()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        log.LogMessage(INFO,"server init ... done");
        return true;
    }

    void Start()
    {
        signal(SIGCHLD,SIG_IGN);
        signal(SIGPIPE,SIG_IGN);
        while(true)
        {
            std::string serverip;
            uint16_t serverport;
            int sockfd = _listensock.Accept(&serverip,&serverport);
            if(sockfd<0)
            {
                continue;
            }
            log.LogMessage(INFO,"accept a new link,sockfd: %d, clientip: %s, clientport: %d",sockfd,serverip.c_str(),serverport);
            //提供服务
            if(fork() == 0)
            {
                _listensock.Close();
                std::string inbuffer_stream;
                //数据计算
                while(true)
                {
                    char buffer[128];
                    ssize_t n = read(sockfd,buffer,sizeof(buffer));
                    if(n>0)
                    {
                        buffer[n] = 0;
                        inbuffer_stream+=buffer;
                        log.LogMessage(DEBUG,"\n%s ",inbuffer_stream.c_str());
                        std::string info = callback_(inbuffer_stream);
                        if(info.empty()) continue;

                        write(sockfd,info.c_str(),info.size());
                    }
                    else if(n == 0) break;
                    else break;

                }

                exit(0);
            }
            close(sockfd);
        }
    }

    ~TcpServer(){}
private:
    uint16_t _port;
    Sock _listensock;
    func_t callback_;
};

Socket.hpp(套接字封装)

#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include <cstring>

enum
{
    SocketErr = 2,
    BindErr,
    ListenErr,
};

const int backlog = 10;

class Sock
{
public:
    Sock() {}
    ~Sock() {}

public:
    void Socket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            log.LogMessage(FATAL, "socket error , %s : %d", strerror(errno), errno);
            exit(SocketErr);
        }
    }

    void Bind(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(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)
        {
            log.LogMessage(FATAL, "bind error , %s : %d", strerror(errno), errno);
            exit(BindErr);
        }
    }

    void Listen()
    {
        if(listen(_sockfd,backlog)<0)
        {
            log.LogMessage(FATAL, "listen error , %s : %d", strerror(errno), errno);
            exit(ListenErr);
        }
    }

    int Accept(std::string *clientip,uint16_t * clientport)
    {   
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int newfd = accept(_sockfd,(struct sockaddr*)&peer,&len);
        if(newfd<0)
        {
            log.LogMessage(WARNING,"accep error, %s : %d",strerror(errno),errno);
            return -1;
        }
        *clientport = ntohs(peer.sin_port);
        char ipstr[64];
        inet_ntop(AF_INET,&(peer.sin_addr),ipstr,sizeof(ipstr));
        *clientip = ipstr;

        return newfd;
    }

    int Connect(const std::string & clientip,const uint16_t & clientport)
    {
        struct sockaddr_in peer;
        memset(&peer,0,sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(clientport);
        inet_pton(AF_INET,clientip.c_str(),&(peer.sin_addr));
        int n = connect(_sockfd,(struct sockaddr*)&peer,sizeof(peer));
        if(n<0)
        {
            std::cerr<<"connect to error ..."<<std::endl;
            return false;
        }

        return true;
    }

    int GetFd()
    {
        return _sockfd;
    }

    void Close()
    {
        close(_sockfd);
    }

private:
    int _sockfd;
};

ServerCal.cc

#include"TcpServer.hpp"
#include"Protocol.hpp"
#include"ServerCal.hpp"

static void Usage(const std::string & proc)
{
    std::cout<<"\nUsage: "<<proc<<" port\n\n"<<std::endl;
}

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        exit(0);
    }

    uint16_t port = std::stoi(argv[1]);
    ServerCal cal;
    TcpServer * tsvp = new TcpServer(8080,std::bind(&ServerCal::Calculator,&cal,std::placeholders::_1));
    tsvp->ServerInit();
    tsvp->Start();
    return 0;
}

ClientCal.cc

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

static void Usage(const std::string & proc)
{
    std::cout<<"\nUsage: "<<proc<<" serverip serverport\n"<<std::endl;
}

int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(0);
    }

    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    Sock sockfd;
    sockfd.Socket();
    bool r = sockfd.Connect(serverip,serverport);
    if(!r) return 1;

    srand(time(nullptr) ^ getpid());
    int cnt = 1;
    const std::string opers = "+-*/%=&^#~";
    std::string inbuffer_stream;
    while(cnt<=20)
    {
        std::cout<<"===============第"<<cnt<<"次测试.....==============="<<std::endl;
        int x = rand()%100+1;
        usleep(1234);
        int y = rand()%100;
        usleep(4124);
        char oper = opers[rand()%opers.size()];

        Request req(x,y,oper);
        req.DebugPrint();

        std::string package;
        req.Serialize(&package);

        package = Encode(package);

        std::cout<<"这是新发出的请求: \n"<<package;

        write(sockfd.GetFd(),package.c_str(),package.size());
      

        char buffer[128];
        ssize_t n = read(sockfd.GetFd(),buffer,sizeof(buffer));
       
        if(n>0)
        {
            buffer[n] = 0;
            inbuffer_stream+=buffer;
          
            std::cout<<inbuffer_stream;
            std::string content;
               
            bool r= Decode(inbuffer_stream,&content);
            
            assert(r);
            
            Response resp;
            r = resp.Deserialize(content);
            assert(r);

            resp.DebugPrint();
        }
        std::cout<<"====================================="<<std::endl;
        sleep(1);
        cnt++;
    }
    sockfd.Close();
    return 0;
}

Makefile

.PHONY:all
all:servercal clientcal

Flag=#-DMySelf=1
Lib=-ljsoncpp

servercal:ServerCal.cc
	g++ -o $@ $^ -std=c++11 $(Lib) $(Flag)
clientcal:ClientCal.cc
	g++ -o $@ $^ -std=c++11 $(Lib) $(Flag)
 
.PHONY:clean
clean:
	rm -f servercal clientcal

运行结果:

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

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

相关文章

Java基于微信小程序的高校体育场管理小程序,附源码

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

什么是态势感知?

什么是态势感知&#xff1f; 同学&#xff0c;听说过态势感知吗&#xff1f;啥&#xff1f;不知道&#xff1f;不知道很正常&#xff0c;因为态势感知是一个比较小众、比较神秘的概念。为什么态势感知很神秘&#xff0c;首先是因为这是来自军事情报领域的概念&#xff0c;然后…

python与设计模式之工厂模式的那些事儿

一、工厂模式 工厂模式实现了按需创建的最佳模式&#xff0c;其目的是为了隐藏创建类的细节与过程&#xff0c;通过一个统一的接口来创建所需的对象。 话说没了皇位争夺权的皇三接到了一个外征的工作&#xff0c;始皇给了5个亿的经费让皇三组建一个军队。打权总是要进行武器采…

测试用例的编写评审

1、什么叫软件测试用例 什么是测试用例 测试用例(TestCase) 是为项目需求而编制的一组测试输入、执行条件 以及预期结果&#xff0c;以便测试某个程序是否满足客户需求。–测试依据 可以总结为:每一个测试点的数据设计和步骤设计。–测试用例 2、测试用例的重要性(了解) 2.1…

从IoTDB的发展回顾时序数据库演进史

面向工业物联网时代&#xff0c;以 IoTDB 为代表的时序数据库加速发展。 时序数据的主要产生来源之一是设备与传感器&#xff0c;具有监测点多、采样频率高、存储数据量大等多类不同于其他数据类型的特性&#xff0c;从而导致数据库在实现高通量写入、存储成本、实时查询等多个…

【数学建模】2024认证杯C题完整思路和代码论文解析

经过不懈的努力&#xff0c;2024认证杯数学建模C题的完整论文和代码已完成&#xff0c;代码为A题全部4问的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建立与求解、问题2模型的建立与求解、问题3模型的建…

Rockchip Android13 Vold(一):Native层

一:概述 Vold全称Volume Daemon是用于管理存储类设备的守护进程,负责接收驱动层设备挂载和卸载消息以及与Framework层之间的通信。Vold作为一个守护进程位于Android的Native Daemons层。 二:Vold框架图 三:Vold Sevice Android13的init.rc位于/system/etc/init/hw/其中使…

域名网络、

http://www.localhost:8080/hello http://127.0.0.1:8080/hello 一般在本机的C:\Windows\System32\drivers\etc的host文件里都有 在这个hosts配置文件中有一个127.0.0.1和默认的用户名locahost&#xff0c;在tomcat启动后输入的地址就是localhost端口号&#xff0c;默认的…

跟bug较劲的第n天,undefined === undefined

前情提要 场景复现 看到这张图片&#xff0c;有的同学也许不知道这个冷知识&#xff0c;分享一下&#xff0c;是因为我在开发过程中踩到的坑&#xff0c;花了三小时排查出问题的原因在这&#xff0c;你们说值不值。。。 我分享下我是怎么碰到的这个问题&#xff0c;下面看代码…

可视化大屏的应用(12):在智慧制造中的十大价值

一、什么是智慧制造 智慧制造是指通过信息技术、物联网、人工智能等先进技术的应用&#xff0c;实现生产过程的数字化、网络化、智能化&#xff0c;从而提高生产效率、降低成本、提高产品质量和灵活性的一种制造模式。 智慧制造的核心是将传统的制造过程与先进的信息技术相结合…

【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割

left mouse button - set rectangle SHIFTleft mouse button - set GC_FGD pixels CTRLleft mouse button - set GC_BGD pixels 这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C程序。它允许用户通过交互式方式选择图像中的一个区域&#xff0c;并利用GrabCut算法尝试…

【大语言模型】基础:如何处理文章,向量化与BoW

词袋模型&#xff08;BoW&#xff09;是自然语言处理&#xff08;NLP&#xff09;和机器学习中一种简单而广泛使用的文本表示方法。它将文本文档转换为数值特征向量&#xff0c;使得可以对文本数据执行数学和统计操作。词袋模型将文本视为无序的单词集合&#xff08;或“袋”&a…

洛谷题单 -- 图论的简单入门

B3643 图的存储 链接 : 图的存储 - 洛谷 思路 : 这一题要考察图的存储方式 , 一般可以使用邻接矩阵 或 邻接表来存储 图的结点 和1 边的信息 &#xff0c;详情请看代码 : 代码 #include<bits/stdc.h> using namespace std;const int N 1010 ; int n , m ; int …

建造者模式:构造复杂对象的艺术

在面向对象的设计中&#xff0c;建造者模式是一种重要的创建型设计模式&#xff0c;专门用来构建复杂的对象。它主要目的是将对象的构造代码与其表示代码分离&#xff0c;使同样的构建过程可以创建不同的表示。本文将详细介绍建造者模式的定义、实现、应用场景以及优缺点&#…

VBA中如何对工作表进行排序

代码 在VBA中对工作表进行排序的最简单方法是直接使用Move方法来移动工作表。 Sub SortSheetsByNameDescending()Dim sheetsDim sheet As WorksheetDim i As Integer, j As IntegerDim sortedSheets() As Array 获取当前工作簿中的所有工作表Set sheets ThisWorkbook.Sheets…

【深入理解Java IO流0x09】解读Java NIO核心知识(下篇)

1. NIO简介 在开始前&#xff0c;让我们再简单回顾一下NIO。 在传统的 Java I/O 模型&#xff08;BIO&#xff09;中&#xff0c;I/O 操作是以阻塞的方式进行的。也就是说&#xff0c;当一个线程执行一个 I/O 操作时&#xff0c;它会被阻塞直到操作完成。这种阻塞模型在处理多…

【研发效能·创享大会-嗨享技术轰趴】-IDCF五周年专场

一、这是一场创新分享局&#xff01; 来吧&#xff0c;朋友们! 参加一场包含AIGC、BizDevOps、ToB产品管理、B端产品运营、平台工程、研发效能、研发度量、职业画布、DevOps国标解读的研发效能创享大会&#xff0c;会有哪些收益呢&#xff1f; 知识更新与技能提升&#xff1a;…

给现有rabbitmq集群添加rabbitmq节点

现有的&#xff1a;10.2.59.216 rabbit-node1 10.2.59.217 rabbit-node2 新增 10.2.59.199 rabbit-node3 1、分别到官网下载erlang、rabbitmq安装包&#xff0c;我得版本跟现有集群保持一致。 erlang安装包&#xff1a;otp_src_22.0.tar.gz rabbitmq安装包&#xff1…

Linux系统启动过程详解

启动过程是指计算机从开机自检到操作系统完全加载的一系列动作。深入理解启动过程对于有效解决启动问题、提升系统性能以及高效管理系统的启动组件至关重要。例如&#xff0c;可以帮助我们识别和处理在启动过程中可能出现的诸如硬件故障、配置错误等问题。例如帮助我们个性化定…

C语言之九九乘法表||素数||最小公倍数

一、九九乘法表 &#xff08;1&#xff09;思路 1、九九乘法表中存在三个变量&#xff0c;以 x1 ; x2 ; y 为例&#xff08;这里也可以使用两个变量&#xff0c;用x1和x2来表示y&#xff0c;方法一样&#xff09; 2、想好了变量之后&#xff0c;我们要想怎样将他实现呢&#x…