【Linux】应用层协议序列化和反序列化

news2024/11/26 22:28:39

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
🌎推荐文章:C++【智能指针】

在这里插入图片描述


前言
在正式代码开始前,会有一些前提知识引入


目录

  • 👉🏻序列化和反序列化
  • 👉🏻三次握手和四次挥手
  • 👉🏻一些概念知识
    • 全双工
    • TCP和UDP对比
    • send和recv
  • 👉🏻TCP通信-使用相同结构化字段传输数据(但未真正的序列化和反序列化)
    • Protocol.hpp(协议内容)
    • Socket.hpp(封装socket通信的功能)
    • TcpServer.hpp(封装服务端功能)
    • TcpServerMain.cc
    • TcpClientMain.cc
    • 实现效果

👉🏻序列化和反序列化

在网络应用层中,序列化(Serialization)和反序列化(Deserialization)是将数据转换为可在网络上传输的格式,并从网络接收的数据恢复为本地数据结构的过程。

🌈序列化(Serialization):

序列化是将数据对象转换为一系列字节的过程,以便在网络上传输或存储到磁盘上。序列化的目标是创建一个可以被发送到其他系统或进程,并且能够被正确解释和还原的数据表示形式。在网络应用中,常见的序列化格式包括JSON(JavaScript Object Notation)、XML(eXtensible Markup Language)、Protocol Buffers、MessagePack等。

序列化的过程通常涉及以下步骤:

  1. 选择序列化格式:根据数据的特性和需求选择合适的序列化格式。
  2. 定义数据结构:确定要序列化的数据对象的结构,并为其定义序列化规则。
  3. 将数据转换为字节流:根据所选的序列化格式,将数据对象转换为字节流。
  4. 传输或存储:将序列化后的字节流发送到网络或者存储到磁盘上。

🌈反序列化(Deserialization):

反序列化是将序列化后的数据流转换回原始数据对象的过程。在接收到网络传输的数据后,需要对其进行反序列化以还原成原始数据对象。

反序列化的过程通常包括以下步骤:

  1. 接收数据流:从网络或者磁盘读取序列化后的数据流。
  2. 解析数据:根据所选的序列化格式,解析字节流并将其转换为数据对象。
  3. 数据还原:根据序列化规则和数据结构,将解析后的数据转换为原始数据对象。
  4. 应用数据:将还原后的数据对象用于应用程序的后续处理。

序列化和反序列化在网络通信中扮演着重要的角色,它们允许不同系统之间以统一的方式进行数据交换,同时也提供了数据传输的可靠性和可扩展性。


📒 一个小故事理解序列化和反序列化

故事标题:糖果工厂的序列化奇遇

一天,糖果工厂的老板决定向全球各地拓展市场,他决定使用一种特殊的糖果序列化器来包装他的糖果,以确保它们在长途运输中保持新鲜和美味。

序列化器(Serializationizer)是一台神奇的机器,它可以将任何形状、口味的糖果转换成一种特殊的串口糖果,这种串口糖果可以轻松地传输到世界各地,并在需要时还原为原始的糖果。

老板向工厂的工程师们解释了他的计划,然后开始了序列化器的操作。首先,他们把一袋五彩斑斓的糖果放进了序列化器中,它发出了一声“嘟噜嘟噜”的声音,然后从另一端输出了一串光滑而有序的串口糖果。

工程师们快乐地向老板展示他们的成果,老板也很满意。于是,他们将这些串口糖果装进了特殊的包装盒,准备发往全球各地的客户。

然而,一名新来的工程师在整理文件时不小心碰到了序列化器的控制台,他误触了一个按钮,导致序列化器的设置发生了改变。

于是,下一个批次的糖果被转换成了一种奇怪的形状,颜色也变得混乱不堪。这些串口糖果被送到了全球各地,但客户们收到后都表示了不满,称他们从未见过如此奇特的糖果。

老板赶紧调查了原因,发现了新工程师的失误。他们及时纠正了序列化器的设置,重新开始了正常的生产。这次,他们确保了每个糖果被正确序列化,而不是变成了像乱七八糟的串口糖果。

结局: 糖果工厂重新获得了客户的信任,全球各地的人们再次享用到了美味的糖果。而那位新工程师也从这个经历中学到了重要的教训:在操作序列化器时一定要小心,否则可能会引发一场糖果灾难!

在这里插入图片描述

👉🏻三次握手和四次挥手

当建立和终止TCP连接时,通常会执行三次握手(Three-Way Handshake)和四次挥手(Four-Way Handshake)的过程,以确保通信的可靠性和正确性。

🌈三次握手(Three-Way Handshake):

  1. 客户端发送同步序列号(SYN)报文:

    • 客户端首先向服务器发送一个带有SYN标志的报文,表示客户端请求建立连接,并选择一个初始的序列号(Sequence Number)。
  2. 服务器确认连接请求:

    • 服务器收到客户端的SYN报文后,会发送一个带有SYN和ACK标志的报文,表示同意建立连接,并确认收到了客户端的连接请求,并选择自己的初始序列号。
  3. 客户端确认连接:

    • 客户端收到服务器的SYN+ACK报文后,会发送一个带有ACK标志的报文给服务器,表示确认连接建立。

这样,客户端和服务器之间的TCP连接就建立起来了,可以开始进行数据传输。

🌈 四次挥手(Four-Way Handshake):

  1. 客户端发送关闭连接请求:

    • 客户端发送一个带有FIN(结束)标志的报文给服务器,表示客户端不再发送数据,但仍愿意接收数据。
  2. 服务器确认关闭请求并关闭数据传输:

    • 服务器收到客户端的FIN报文后,会发送一个带有ACK标志的报文给客户端,表示确认关闭请求,并停止向客户端发送数据,但仍可以接收数据。
  3. 服务器发送关闭连接请求:

    • 服务器发送一个带有FIN标志的报文给客户端,表示服务器也准备关闭连接。
  4. 客户端确认关闭请求并关闭连接:

    • 客户端收到服务器的FIN报文后,会发送一个带有ACK标志的报文给服务器,表示确认关闭请求,并关闭连接。

这样,客户端和服务器之间的TCP连接就完全关闭了。四次挥手的过程中,双方都可以发送数据,并且在关闭连接后都不能再发送数据。


📒 一个小例子理解三次握手和四次挥手
好的,让我们用一种有趣的方式来理解三次握手和四次挥手。

🫱🏻🫲🏻 三次握手(Three-Way Handshake):

想象一下你和朋友约好去吃披萨。这里有个名叫小明的朋友(客户端)和一个叫披萨店的地方(服务器)。

  1. 小明: “嗨,披萨店老板!我想要一份披萨!”(发送SYN)

  2. 披萨店老板: “好的,小明,你想要什么口味的披萨?”(发送SYN+ACK)

  3. 小明: “我想要意大利香肠披萨!”(发送ACK)

现在,小明和披萨店之间建立了连接,披萨店知道了小明的口味,准备开始制作披萨。

🤲🏻 四次挥手(Four-Way Handshake):

披萨终于做好了,大家都吃得很开心,然后就是结束这次美好的披萨时光。

  1. 小明: “披萨店老板,谢谢你的披萨,我不想再点了!”(发送FIN)

  2. 披萨店老板: “不客气,小明,欢迎下次再来!披萨店休息了!”(发送ACK)

  3. 披萨店老板: “好了,披萨店打烊了,我们关门了!”(发送FIN)

  4. 小明: “明白了,披萨店老板,再见!”(发送ACK)

这样,小明和披萨店之间的交流就结束了,披萨店可以关门休息了,而小明也满足地离开了。
在这里插入图片描述

👉🏻一些概念知识

全双工

全双工(Full Duplex)是指数据通信系统中能够同时实现双向通信的能力,即在同一时间点上可以同时进行发送和接收数据的操作。这种模式下,通信双方能够同时进行双向数据传输,而不需要等待对方完成发送或接收操作。

在全双工通信中,发送和接收数据的通道是完全独立的,彼此之间互不干扰。这意味着通信双方可以在不同的频率或者不同的频道上同时进行通信,而不会造成碰撞或数据丢失。

全双工通信通常用于需要高速、实时双向数据传输的场景,比如电话通话、视频会议、网络通信等。相比于半双工通信(Half Duplex),全双工通信具有更高的通信效率和更低的延迟,因为它允许发送和接收数据同时进行,而不需要等待切换操作。

在网络通信中,全双工模式通常通过使用不同的通信频率(如Wi-Fi、蓝牙等无线通信)、不同的通信信道(如以太网的双绞线)或者使用不同的时隙(如时分多址技术)来实现。这种模式在现代通信技术中被广泛应用,为用户提供了更流畅、更高效的通信体验。

TCP和UDP对比

TCP(传输控制协议)和UDP(用户数据报协议)是两种常用的网络传输协议,它们在数据传输时有着不同的特点和适用场景:

  1. 连接性:

    • TCP是面向连接的协议,它在通信双方建立连接后才能进行数据传输,确保数据的可靠性和顺序性。
    • UDP是无连接的协议,通信双方无需建立连接即可直接发送数据,因此不保证数据的可靠性和顺序性。
  2. 可靠性:

    • TCP提供可靠的数据传输,通过序号、确认和重传机制来确保数据的完整性和可靠性,保证数据不会丢失或损坏。
    • UDP不提供可靠性保证,数据包可能会丢失、重复或者乱序,因此在一些实时性要求高、但对数据完整性要求较低的场景下使用较多。
  3. 流量控制(面向字节流)和拥塞控制:

    • TCP通过流量控制和拥塞控制机制来调节数据传输速率,以避免网络拥塞和数据丢失。
    • UDP不提供流量控制和拥塞控制,数据传输速率由发送方直接决定,可能会导致网络拥塞。
  4. 适用场景:

    • TCP适用于需要可靠数据传输和顺序传输的场景,如文件传输、网页浏览、电子邮件等。
    • UDP适用于实时性要求高、但对数据完整性要求较低的场景,如音频和视频流、在线游戏、实时通信等。
  5. 开销:

    • TCP的头部开销较大,包含了序号、确认、窗口大小等信息,因此在传输小量数据时可能会存在较大的开销。
    • UDP的头部开销较小,只包含了源端口、目标端口、长度和校验和等基本信息,因此在传输小量数据时开销较小。

总的来说,TCP提供了可靠的数据传输和顺序传输,适用于对数据完整性要求高的场景;而UDP提供了更快速的数据传输和更低的开销,适用于实时性要求高、但对数据完整性要求较低的场景。选择使用哪种协议取决于具体的应用需求和性能要求。

send和recv

当编写网络程序时,常用的函数之一是sendrecv,它们通常用于在TCP连接上发送和接收数据。

🍓send 函数:

  • 功能: 用于在已建立的连接上发送数据。
  • 语法: send(socket, data, flags)
    • socket:指定发送数据的套接字。
    • data:要发送的数据。
    • flags:指定发送操作的可选标志。

🍓recv 函数:

  • 功能: 用于从已建立的连接上接收数据。
  • 语法: recv(socket, buffersize, flags)
    • socket:指定接收数据的套接字。
    • buffersize:指定接收缓冲区的大小。
    • flags:指定接收操作的可选标志。

这两个函数在TCP编程中非常常见,它们允许程序在客户端和服务器之间进行双向通信。

🍴 与sendto 函数和 recvfrom 函数的区别:

  • sendto 函数: 用于在无连接的套接字上发送数据。通常用于UDP套接字。
    • 它需要指定目标地址和端口。
  • recvfrom 函数: 用于从无连接的套接字上接收数据。通常用于UDP套接字。
    • 它返回发送数据的源地址和端口。

在网络编程中,write和read函数通常用于TCP套接字,因此它们也是基于已连接的。

总的来说,sendrecv函数适用于TCP连接,而sendtorecvfrom函数适用于UDP套接字。前者是基于连接的,后者是无连接的。

👉🏻TCP通信-使用相同结构化字段传输数据(但未真正的序列化和反序列化)

代码目录:
在这里插入图片描述

Protocol.hpp(协议内容)

#pragma once

#include<iostream>
#include<memory>
using namespace std;

class Request
{
public:
    Request()
    {}
    Request(int x,int y,char op)
    :_data_x(x),_data_y(y),_oper(op)
    {}


    void Inc()
    {
        _data_x++;
        _data_y++;
    }
    void Debug()
    {
        cout<<"_data_x: "<<_data_x<<endl;
        cout<<"_data_y: "<<_data_y<<endl;
        cout<<"_oper: "<<_oper<<endl;
    }


private:
    int _data_x;
    int _data_y;
    char _oper;//操作数
};

class Response
{
public:
    Response()
    {}
    Response(int result,int code)
    :_result(result),_code(code){

    }
private:
    int _result;
    int _code;
};

//工厂模式,建造类设计模式,直接返回指针对象
class Factory
{
public:
    shared_ptr<Request> BuildRequest()
    {
        shared_ptr<Request> req = make_shared<Request>();
        return req;
    }
    
       shared_ptr<Request> BuildRequest(int x,int y,char op)
       {
        shared_ptr<Request> req = make_shared<Request>(x,y,op);
        return req;
    }

    shared_ptr<Response> BuildResponse()
    {
        shared_ptr<Response> resp = make_shared<Response>();

        return resp;
    }
      shared_ptr<Response> BuildResponse(int result,int code)
    {
        shared_ptr<Response> resp = make_shared<Response>(result,code);

        return resp;
    }

};

Socket.hpp(封装socket通信的功能)

#pragma once 

#include<iostream>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define Convert(addrptr) ((struct sockaddr*)addrptr)

using namespace std;

namespace Net_Work
{
    const static int defaultsockfd = -1;
    const int backlog = 5;
    enum
    {
        SocketError = 1,
        BindError,
        ListenError,
    };
    //封装一个基类:Socket接口类
    class Socket
    {
    public:
        virtual ~Socket(){}
        virtual void CreateSocketOrDie() = 0;//创建一个套接字
        virtual void BindSocketOrDie(uint16_t port) = 0;//套接字进行绑定网络信息
        virtual void ListenSocketOrDie(int backlog) = 0;//进行监听
        virtual Socket* AcceptConnection(string * peerip,uint16_t* peerport)=0;//接收连接,并返回一个新的套接字
        virtual bool ConnectServer(string& peerip,uint16_t peerport)=0;//连接服务端
        virtual int GetSockFd() = 0;//返回套接字描述符
        virtual void SetSockFd(int sockfd) = 0;//
        virtual void CloseSocket() = 0;//关闭套接字
    public:
        void BuildListenSocketMethod(uint16_t port,int backlog)//创建一个监听服务
        {
            //1.创建套接字
            CreateSocketOrDie();
            //2.套接字进行绑定网络信息
            BindSocketOrDie(port);
            //3.开始监听
            ListenSocketOrDie(backlog);
        }
        bool BuildConnectSocketMethod(string& serverip,uint16_t& serverport)//创建一个连接服务
        {
            //1.创建套接字
            CreateSocketOrDie();
            return ConnectServer(serverip,serverport);

        }
        void BuildNormalSocketMethod(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    //实现Tcp套接字
    class TcpSocket:public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd )
        :_sockfd(sockfd)
        {

        }
        ~TcpSocket(){}
        /
       
         void CreateSocketOrDie() override//创建一个套接字
         {
            _sockfd = socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd<0)
                exit(SocketError);
         }

         void BindSocketOrDie(uint16_t port) override//套接字进行绑定网络信息
         {
            //本地网络信息初始化
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;//服务端的ip由本地随机绑定
            local.sin_port = htons(port);

            //开始绑定
            int n = bind(_sockfd,Convert(&local),sizeof(local));
            if(n<0) exit(BindError);
         }
         void ListenSocketOrDie(int backlog) override//进行监听
         {
            int n = listen(_sockfd,backlog);
            if(n<0) exit(ListenError);
         }
        Socket* AcceptConnection(string * peerip,uint16_t* peerport)override//接收连接
        {
            struct sockaddr_in peer;//用来存储客户端的地址信息
            socklen_t len = sizeof(peer);
            int newsockfd = accept(_sockfd,Convert(&peer),&len);
            if(newsockfd<0)
                return nullptr;

            *peerport = ntohs(peer.sin_port);//网络序列本地化
            *peerip = inet_ntoa(peer.sin_addr);

            Socket* s = new TcpSocket(newsockfd);
            return s;
        }
         bool ConnectServer(string& serverip,uint16_t serverport)override//连接服务端
         {
            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());//ip网络字节序化,4字节

            int n = connect(_sockfd,Convert(&server),sizeof(server));
            if(n==0)
                return true;
            else
                return false;
         }
         int GetSockFd() override//返回套接字描述符
         {
            return _sockfd;
         }
         void SetSockFd(int sockfd) override//
         {
            _sockfd = sockfd;
         }
         void CloseSocket() override//关闭套接字
         {
            if(_sockfd>defaultsockfd)
                close(_sockfd);
         }
    private:
        int _sockfd;
    };
};

TcpServer.hpp(封装服务端功能)

#pragma once

#include"Socket.hpp"
#include<pthread.h>
#include<functional>

using func_t = function<void(Net_Work::Socket* sockp)>;

class TcpServer;

class ThreadData
{
public:
    ThreadData(TcpServer* tcp_this,Net_Work::Socket* sockp)
    :_this(tcp_this),_sockp(sockp)
    {}
public:
    TcpServer* _this;//TcpServer的指针对象
    Net_Work::Socket* _sockp;//套接字指针对象
};


class TcpServer
{
public:
    TcpServer(uint16_t port,func_t handler_request)
    :_port(port),_listensocket(new Net_Work::TcpSocket()),_hanlder_request(handler_request)
    {
        _listensocket->BuildListenSocketMethod(_port,Net_Work::backlog);//开启监听事务
    }
    static void * ThreadRun(void* args)//因为pthread_create要求方法参数中的参数必须只有一个void*
    //所以必须变为静态,否则成员函数第一个参数默认隐式为this指针
    {
        //因为执行的是多线程,这里我们也没有封装线程的自动回收
        //所以为了不发生线程阻塞,我们要让当前线程与主线程分离,不影响主线程,并且自己做完任务自己回收
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
       
        td->_this->_hanlder_request(td->_sockp);//执行_hanlder_request方法

        td->_sockp->CloseSocket();//关闭accept的新套接字
        delete td->_sockp;//销毁指针
        delete td;
        return nullptr;
    }
    void Loop()
    {
         while(true)
        {
            string peerip;
             uint16_t peerport;
            Net_Work::Socket* newsocket = _listensocket->AcceptConnection(&peerip,&peerport);//接收客户端信息
            if(newsocket==nullptr) continue;
                 cout<<"获取一个新连接,sockfd:"<<newsocket->GetSockFd()<<"client info: "<<peerip<<" "<<peerport<<endl;

            //用完后关闭newsocket
            //newsocket->CloseSocket();   

            //使用多线程进行处理任务
            pthread_t tid;
            ThreadData* td = new ThreadData(this,newsocket);
            pthread_create(&tid,nullptr,ThreadRun,td);//线程创建并执行相对应任务
         }
    }

    ~TcpServer()
    {
        delete _listensocket;
    }

private:
    uint16_t _port;
    Net_Work::Socket* _listensocket;

public:
    func_t _hanlder_request;//request执行方法

};

TcpServerMain.cc

#include"Protocol.hpp"
#include"Socket.hpp"
#include"TcpServer.hpp"
#include<memory>

using namespace Net_Work;
void HandlerRequest(Socket* sockp)
{
    while(true)
    {
        struct Request req;//用来存储客户端发来的需求信息
        recv(sockp->GetSockFd(),&req,sizeof(req),0);//接收
        req.Debug();//打印信息
    }
}

int main(int argc,char* argv[])
{
     if(argc != 2)
    {
        cout << "Usage : " << argv[0] << " port" << std::endl;
        return 0;
    }
    uint16_t  localport = stoi(argv[1]);

    unique_ptr<TcpServer> svr (new TcpServer(localport,HandlerRequest));//unique_ptr只能支持移动构造

    svr->Loop();//server开始不断获取新连接
    return 0;
}

TcpClientMain.cc

#include"Protocol.hpp"
#include"Socket.hpp"


int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        cout << "Usage : " << argv[0] << " serverip serverport" << std::endl;
        return 0;
    }
    string serverip = argv[1];
    uint16_t  serverport = stoi(argv[2]);

    Net_Work::Socket* s = new Net_Work::TcpSocket();
    if(!s->BuildConnectSocketMethod(serverip, serverport))
    {
        cerr << "connect " << serverip << ":" << serverport << " failed" << std::endl;
    }
    cout << "connect " << serverip << ":" << serverport << " success" << std::endl;

    unique_ptr<Factory> factory = make_unique<Factory>();//创建一个工厂对象指针(后续可以生产需求和回应),工厂只能有一个,所以用unique_ptr指针

    shared_ptr<Request> req = factory->BuildRequest(10,20,'+');

    while(true)
    {
        req->Inc();
        send(s->GetSockFd(),&(*req),sizeof(*req),0);//将需求信息发送给服务端
        sleep(1);
    }

    s->CloseSocket();//关闭套接字
    return 0;
}

实现效果

在这里插入图片描述


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

设计模式———单例模式

单例也就是只能有一个实例&#xff0c;即只创建一个实例对象&#xff0c;不能有多个。 可能会疑惑&#xff0c;那我写代码的时候注意点&#xff0c;只new一次不就得了。理论上是可以的&#xff0c;但在实际中很难实现&#xff0c;因为你无法预料到后面是否会脑抽一下~~因此我们…

北京InfoComm China 2024今日开幕:重新定义人机交互

2024年4月17日&#xff0c;北京——北京InfoComm China 2024展会在万众期待中于北京国家会议中心盛大开幕。这场为期三天的行业盛会&#xff0c;将持续至4月19日&#xff0c;为参观者们带来一场前所未有的创新技术与解决方案的盛宴。 本届展会作为亚太地区领先的专业视听与集成…

本地jar包手动添加到Maven仓库

步骤&#xff1a; 打开命令行窗口&#xff08;Windows&#xff09;或终端&#xff08;macOS/Linux&#xff09;。使用cd命令导航到包含jar文件的目录。执行以下命令&#xff0c;将jar文件添加到Maven本地仓库&#xff1a; mvn install:install-file -Dfile<jar文件名>.…

Swift Publisher 5 for mac:打造精美版面

Swift Publisher 5 for mac&#xff1a;打造精美版面 Swift Publisher 5是一款专业的版面设计和编辑工具&#xff0c;为Mac用户提供了强大的设计功能和直观的操作界面。以下是关于Swift Publisher 5的功能介绍&#xff1a; 直观易用的界面&#xff1a;用户能够轻松地使用Swift …

图书管理系统概述

自友图书馆管理系统解决方案适用于中小学、大中专院校以及企事业单位中小型图书馆的自动化管理需求&#xff0c;其功能覆盖了图书馆自动化集成管理业务流程所包括的所有环节。《图书馆管理系统》首先应该按照我国图书馆行业通用CNMARC格式及《中图法第四版》行业标准开发而成,支…

【网安小白成长之路】6.pikachu、sql-labs、upload-labs靶场搭建

&#x1f42e;博主syst1m 带你 acquire knowledge&#xff01; ✨博客首页——syst1m的博客&#x1f498; &#x1f51e; 《网安小白成长之路(我要变成大佬&#x1f60e;&#xff01;&#xff01;)》真实小白学习历程&#xff0c;手把手带你一起从入门到入狱&#x1f6ad; &…

57.网络游戏逆向分析与漏洞攻防-基础数据分析筛选-获取他人发来的聊天信息

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&…

从二本调剂到上海互联网公司算法工程师:我的成长故事

探讨选择成为一名程序员的原因&#xff0c;是出于兴趣还是职业发展&#xff1f; 在这个科技飞速发展的时代&#xff0c;程序员这一职业无疑成为了许多人眼中的香饽饽。那么&#xff0c;是什么驱使着越来越多的人选择投身于这一行业呢&#xff1f;是出于对编程的热爱&#xff0…

计算机网络基础:宏观认识

目录 一、网络发展背景与基本概念 二、网络协议的意义与TCP/IP五层结构模型 三、网络传输的基本流程与封装分用 四、ip地址和mac地址 随着信息技术的飞速发展&#xff0c;计算机网络已经成为了现代社会不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们几乎都离…

MySql中truncate,delete有什么区别?什么情况下id会不会连续呢?

TRUNCATE和DELETE都是用来删除表中数据的SQL命令&#xff0c;但它们的工作方式和使用场景有所不同&#xff1a; DELETE命令&#xff1a;DELETE命令用于从表中删除一行、多行或所有行。你可以添加WHERE子句来指定要删除的行。例如&#xff0c;DELETE FROM table_name WHERE cond…

Java项目实现Excel导出(Hutool)

官网&#xff1a; Excel生成-ExcelWriter (hutool.cn) 1.使用Hutool工具实现Excel导出&#xff08;.xlsx格式&#xff09; 业务场景&#xff1a; 使用SpringCloudmysqlmybatis-plus需要将数据库中的数据导出到Excel文件中 前端为Vue2 第零步&#xff1a;导入依赖 <!-…

[Win11·Copilot] Win11 系统更新重启后任务栏 Copilot 图标突然消失 | 解决方案

文章目录 前言Copilot介绍产生异常的原因解决方案总结 前言 在 Windows 11 的最新系统更新之后&#xff0c;一些用户报告了任务栏中 Copilot 图标消失的问题。这篇技术博文将为您提供详细的解决方案&#xff0c;帮助您恢复 Copilot 图标&#xff0c;并确保您能够继续享受 Copi…

计算机网络-IS-IS基础概念二

前面已经学习了IS-IS的定义、组成、NET地址标识以及路由器级别分类等&#xff0c;今天继续学习IS-IS基础概念知识。 参考链接&#xff1a;IS-IS路由协议基础概念 一、IS-IS支持的网络类型 IS-IS会自动根据接口的数据链路层封装决定该接口的缺省网络类型&#xff0c; IS-IS支持两…

list基础知识

list 1.list 的定义和结构 list 是双向链表&#xff0c;是C的容器模板&#xff0c;其接收两个参数&#xff0c;即 list(a,b) 其中 a 表示指定容器中存储的数据类型&#xff0c;b 表示用于分配器内存的分配器类型&#xff0c;默认为 list <int>; list 的特点&#xff1a;…

机器学习和深度学习--李宏毅 (笔记与个人理解)Day 16

Day 16 deep Learning – 鱼与熊掌兼得 最近在减重&#xff0c; 昨天跑了个一公里&#xff0c;然后今天上午又打了个篮球&#xff0c;真是老胳膊老腿了&#xff0c;运动完给我困得不行 Review 见前面的笔记 这里说dl 会提供一个足够大的模型&#xff0c; 来使得Dall loss 足够小…

Mac 利用Homebrew安装JDK

一、安装JDK17 1.安装openjdk17 2.把homebrew安装的openjdk17软链接到系统目录&#xff1a; brew install openjdk17 sudo ln -sfn $(brew --prefix)/opt/openjdk17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk 一、检查是否安装成功 在Termina…

python使用uiautomator2操作真机(oppo a9x)

环境&#xff1a; python3.8.10&#xff0c;oppo a9x(6G,128g)&#xff0c;版本android 11。 之前写过文章&#xff1a; python使用uiautomator2操作真机&#xff08;荣耀10青春版&#xff09;_python uiautomator2 控制真机-CSDN博客 python使用uiautomator2操作真机&…

每日两题 / 189. 轮转数组 560. 和为 K 的子数组(LeetCode热题100)

189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; 向右轮转将使尾部k个元素顶到头部 将整个数组反转&#xff0c;再分别反转前k个元素和剩下的元素即可 class Solution { public:void rotate(vector<int>& nums, int k) {k % nums.size();reverse(nums.begi…

RHCE2

一.配置server主机要求如下&#xff1a; 1.server主机的主机名称为 ntp_server.example.com //修改主机名 hostnamectl set-hostname ntp_server.example.comreboot 修改主机名后一定要记住重启。 2.server主机的IP为&#xff1a; 172.25.254.100 //主机ip配置 [rootntpse…

Java实现生成中间带图标的二维码

Java实现生成中间带图标的二维码 生成Base64格式的二维码&#xff0c;返回html渲染 package your.package;import com.google.zxing.*; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcod…