Linux网络——自定义序列化与反序列化

news2024/12/24 9:30:29

前言

之前我们学习过socket之tcp通信,知道了使用tcp建立连接的一系列操作,并通过write与read函数能让客户端与服务端进行通信,但是tcp是面向字节流的,有可能我们write时只写入了部分数据,此时另一端就来read了,可能会导致读取的数据不完整的问题。这就引入到tcp通信的一个重要操作了——序列化与反序列化

一、使用结构体进行传输

在我们之前学习的代码中,一般想传递很多数据,都会使用到结构体。

比如pthread_create()需要传递函数地址,你如果需要传递很多参数,就得创建一个结构体,将他们组织起来,传递这个结构体指针给到pthread_create(),因为他第四个参数只能接受void*指针。不是可变参数包。

 如下,制作一个网络计算器,我们客户端创建结构体数据并发送

服务端对数据进行接受

这样服务器就可以接受到数据并做相应的处理了,这里我们显示出来表示接收到了数据,处理起来也就很简单了。

结构体代码链接

二、自定义序列化与反序列化

tcp中的数据使用结构体传输在网络压力很大,或者传输内容很多时,就会发生问题。同样在不同的编译环境下,结构体内存对齐也不一致,并且有可能客户端是使用其他代码进行编写,C/C++的结构体也不会适合。

  • 如果我们将传输的数据统一处理成字符串,让字符串在tcp中进行传输,同时设置一些固定的字段,代表当前数据包的结束,是不是能解决上述问题呢?
  • 这就是数据序列化后进行传输,接收方将数据进行反序列化后提取再做处理序列化的本质就是对字符串作处理

一样的计算器,一样的需要传递两个整形数字与一个字符。

  • 如果我们将该结构体转为字符串,如 "x oper y\n" ,这样我们就可以通过读取空格来表示取到的整形,字符,整形,如果一直没读到"\n"证明当前数据包还没读完,读到"\n"证明取到了一个完整的数据包,这样就可以完美解决问题了。
  • 但是我们这个写法没有普适性,因为如果后面如果结构体内容比较复杂,带了一个"\n",就会导致序列化代码需要重新设计。如果在前面添加一个报头len,代表后面内容的长度,这样无论后面代码是什么,都可以统一看待处理。
  • 于是,我们得到如下的序列化公式。

"len\nx op y"

这里len后面的"\n"代表我们读取到了len数据,后面就是真正要传输并处理的数据了。

了解了这些,那么我们进行网络计算器的序列化与反序列化也就不难了,代码如下

Protocol.hpp  自定义协议

#pragma once

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

namespace Protocol
{
    const string ProtSep = " ";
    const string LineBreakSep = "\n";

    // message 现在我要给他头部添加len\n 尾部添加\n
    // 如"x op y"-> "len\nx op y\n"   "result code"-> "len\nresult code\n"
    string Encode(string &message)
    {
        string len = to_string(message.size());
        string package = len + LineBreakSep + message + LineBreakSep;
        return package;
    }

    // 从一个完整的package进行解析,"len\nx op y\n"->"x op y"
    bool Decode(string &package, string *message)
    {
        int left = package.find(LineBreakSep);
        if (left == string::npos)
            return false;
        int len = stoi(package.substr(0, left));
        int total = left + len + 2 * LineBreakSep.size();
        if (package.size() < total)
            return false;
        // 到这里至少有一个完整的报文 截取出来一个报文写入message,然后删除该报文
        *message = package.substr(left + LineBreakSep.size(), len);
        package.erase(0, total);
        return true;
    }

    class Request
    {
    public:
        Request() : _data_x(0), _data_y(0), _oper(0)
        {
        }
        Request(int x, int y, char op)
            : _data_x(x), _data_y(y), _oper(op)
        {
        }
        void Debug()
        {
            cout << "_data_x: " << _data_x << endl;
            cout << "_data_y: " << _data_y << endl;
            cout << "_oper: " << _oper << endl;
        }
        void Inc()
        {
            _data_x++;
            _data_y++;
        }

        // 序列化  变成这种字符串"x op y"
        void Serialize(string *out)
        {
            *out = to_string(_data_x) + ProtSep + _oper + ProtSep + to_string(_data_y);
        }
        // 反序列化  "x op y"字符串分别取出
        bool Deserialize(string &in)
        {
            size_t left = in.find(ProtSep);
            if (left == string::npos)
                return false;
            size_t right = in.rfind(ProtSep);
            if (right == string::npos)
                return false;
            _data_x = stoi(in.substr(0, left));
            _data_y = stoi(in.substr(right + ProtSep.size()));
            string op = in.substr(left + ProtSep.size(), right - left - ProtSep.size());
            if (op.size() != 1)
                return false;
            _oper = op[0];
            return true;
        }
        int Getx()
        {
            return _data_x;
        }
        int Gety()
        {
            return _data_y;
        }
        char Getop()
        {
            return _oper;
        }

        string To_string()
        {
            return to_string(_data_x)+" "+_oper+ " "+to_string(_data_y)+" = ";
        }

    private:
        // x oper y 比如 10 + 20
        int _data_x;
        int _data_y;
        char _oper; // 符号
    };

    class Response
    {
    public:
        Response() : _result(0), _code(0)
        {
        }
        Response(int result, int code)
            : _result(result), _code(code)
        {
        }
        void Serialize(string *out)
        {
            //_result _code
            *out = to_string(_result) + ProtSep + to_string(_code);
        }
        bool Deserialize(string &in)
        {
            size_t pos = in.find(ProtSep);
            if (pos == string::npos)
                return false;
            _result = stoi(in.substr(0, pos));
            _code = stoi(in.substr(pos + ProtSep.size()));
            return true;
        }
        int Getresult()
        {
            return _result;
        }
        int Getcode()
        {
            return _code;
        }
        void Setresult(int result)
        {
            _result = result;
        }
        void Setcode(int code)
        {
            _code = code;
        }

    private:
        int _result; // 运算结果
        int _code;   // 运算状态
    };

    // 工厂模式
    class Factory
    {
    public:
        shared_ptr<Request> BuildRequest()
        {
            return make_shared<Request>();
        }
        shared_ptr<Request> BuildRequest(int x, int y, char op)
        {
            return make_shared<Request>(x, y, op);
        }
        shared_ptr<Response> BuildResponse()
        {
            return make_shared<Response>();
        }
        shared_ptr<Response> BuildResponse(int result, int code)
        {
            return make_shared<Response>(result, code);
        }
    };
}

 Socket.hpp 套接字函数封装

#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
using namespace std;
namespace Net_Work
{
    static const int default_backlog = 5;
    static const int default_sockfd = -1;
    using namespace std;

    enum
    {
        SocketError = 1,
        BindError,
        ListenError,
        ConnectError,
    };

    // 封装套接字接口基类
    class Socket
    {
    public:
        // 封装了socket相关方法
        virtual ~Socket() {}
        virtual void CreateSocket() = 0;
        virtual void BindSocket(uint16_t port) = 0;
        virtual void ListenSocket(int backlog) = 0;
        virtual bool ConnectSocket(string &serverip, uint16_t serverport) = 0;
        virtual Socket *AcceptSocket(string *peerip, uint16_t *peerport) = 0;
        virtual int GetSockFd() = 0;
        virtual void SetSockFd(int sockfd) = 0;
        virtual void CloseSocket() = 0;
        virtual bool Recv(string *buff, int size) = 0;
        virtual void Send(string &send_string) = 0;

        // 方法的集中在一起使用
    public:
        void BuildListenSocket(uint16_t port, int backlog = default_backlog)
        {
            CreateSocket();
            BindSocket(port);
            ListenSocket(backlog);
        }

        bool BuildConnectSocket(string &serverip, uint16_t serverport)
        {
            CreateSocket();
            return ConnectSocket(serverip, serverport);
        }

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

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = default_sockfd)
            : _sockfd(sockfd)
        {
        }
        ~TcpSocket() {}

        void CreateSocket() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
                exit(SocketError);
        }
        void BindSocket(uint16_t port) override
        {
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

            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;

            int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n < 0)
                exit(BindError);
        }
        void ListenSocket(int backlog) override
        {
            int n = listen(_sockfd, backlog);
            if (n < 0)
                exit(ListenError);
        }
        bool ConnectSocket(string &serverip, uint16_t serverport) override
        {
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            addr.sin_family = AF_INET;
            addr.sin_port = htons(serverport);
            // addr.sin_addr.s_addr = inet_addr(serverip.c_str());
            inet_pton(AF_INET, serverip.c_str(), &addr.sin_addr);
            int n = connect(_sockfd, (sockaddr *)&addr, sizeof(addr));

            if (n == 0)
                return true;
            return false;
        }
        Socket *AcceptSocket(string *peerip, uint16_t *peerport) override
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int newsockfd = accept(_sockfd, (sockaddr *)&addr, &len);
            if (newsockfd < 0)
                return nullptr;

            // *peerip = inet_ntoa(addr.sin_addr);

            // INET_ADDRSTRLEN 是一个定义在头文件中的宏,表示 IPv4 地址的最大长度
            char ip_str[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &addr.sin_addr, ip_str, INET_ADDRSTRLEN);
            *peerip = ip_str;

            *peerport = ntohs(addr.sin_port);

            Socket *s = new TcpSocket(newsockfd);
            return s;
        }
        int GetSockFd() override
        {
            return _sockfd;
        }
        void SetSockFd(int sockfd) override
        {
            _sockfd = sockfd;
        }
        void CloseSocket() override
        {
            if (_sockfd > default_sockfd)
                close(_sockfd);
        }

        bool Recv(string *buff, int size) override
        {
            char inbuffer[size];
            ssize_t n = recv(_sockfd, inbuffer, size - 1, 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                *buff += inbuffer;
                return true;
            }
            else
                return false;
        }

        void Send(string &send_string) override
        {
            send(_sockfd, send_string.c_str(),send_string.size(),0);
        }

    private:
        int _sockfd;
        string _ip;
        uint16_t _port;
    };
}

Calculate.hpp  计算业务封装

#pragma once

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

enum ErrCode
{
    Sucess = 0,
    DivZeroErr,
    ModZeroErr,
    UnKnowOper,
};


//计算业务
class Calculate
{
public:
    Calculate()
    {
    }
    shared_ptr<Protocol::Response> Cal(shared_ptr<Protocol::Request> req)
    {
        shared_ptr<Protocol::Response> resp = factory.BuildResponse();
        resp->Setcode(Sucess);
        switch (req->Getop())
        {
        case '+':
            resp->Setresult(req->Getx() + req->Gety());
            break;
        case '-':
            resp->Setresult(req->Getx() - req->Gety());

            break;
        case '*':
            resp->Setresult(req->Getx() * req->Gety());
            break;
        case '/':
        {
            if (req->Gety() == 0)
                resp->Setcode(DivZeroErr);
            else
                resp->Setresult(req->Getx() / req->Gety());
        }

        break;
        case '%':
        {
            if (req->Gety() == 0)
                resp->Setcode(ModZeroErr);
            else
                resp->Setresult(req->Getx() % req->Gety());
        }
        break;

        default:
            resp->Setcode(UnKnowOper);
            break;
        }
        return resp;
    }

private:
    Protocol::Factory factory;
};

 TcpServer.hpp  tcp服务端封装

#pragma once

#include "Protocol.hpp"
#include "Socket.hpp"
#include <thread>
#include <functional>

using func_t = function<string(string &inbufferstream, bool *error_code)>;

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t hander_request)
        : _port(port), _listen_socket(new Net_Work::TcpSocket()), _handler_request(hander_request)
    {
        _listen_socket->BuildListenSocket(port);
    }

    void ThreadRun(Net_Work::Socket *s)
    {

        // cout<< " in HandlerRequest !"<<endl;
        string inbufferstream;
        while (true)
        {
            bool resultcode = true;
            // 1.读取报文
            if (!s->Recv(&inbufferstream, 1024))
                break;

            // 2.报文处理
            string send_string = _handler_request(inbufferstream, &resultcode);
            if (resultcode)
            {
                // 发送数据
                if (!send_string.empty())
                {
                    s->Send(send_string);
                }
            }
            else
            {
                break;
            }
        }

        s->CloseSocket();
        delete s;
    }

    void Loop()
    {
        while (true)
        {
            string peerip;
            uint16_t peerport;
            Net_Work::Socket *newsockfd = _listen_socket->AcceptSocket(&peerip, &peerport);
            if (newsockfd == nullptr)
                continue;
            cout << "获取一个新链接, sockfd: " << newsockfd->GetSockFd() << " ,client info: " << peerip << ":" << peerport << endl;

            thread t1(&TcpServer::ThreadRun, this, newsockfd);
            t1.detach();
        }
    }
    ~TcpServer()
    {
        delete _listen_socket;
    }

private:
    int _port;
    Net_Work::Socket *_listen_socket;
    func_t _handler_request;
};

Main.cc   服务端实现

#include "TcpServer.hpp"
#include "Calculate.hpp"
#include <unistd.h>
#include <memory>

string HandlerRequest(string &inbufferstream, bool *error_code)
{

    Calculate calculate;

    // 1.创建请求对象
    unique_ptr<Protocol::Factory> factory(new Protocol::Factory());
    shared_ptr<Protocol::Request> req = factory->BuildRequest();

    string total_resp_string;
    // 2.判断字节流是否读取到了一个完整的报文  把报文读完
    string message;
    while (Protocol::Decode(inbufferstream, &message))
    {
        // 3.这里已经读到完整的报文,需要进行反序列化  反序列化失败证明是非法请求
        if (!req->Deserialize(message))
        {
            // 反序列化失败  不能忍受,因为客户端的请求是不合法的
            *error_code = false;
            return string();
        }
        // 4.业务处理
        shared_ptr<Protocol::Response> resp = calculate.Cal(req);

        // 5.序列化
        string send_string;
        resp->Serialize(&send_string);

        // 6.添加报头
        send_string = Protocol::Encode(send_string);

        total_resp_string+=send_string;
    }

    return total_resp_string;
}

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

    unique_ptr<TcpServer> tsvr(new TcpServer(localport, HandlerRequest));
    tsvr->Loop();
    return 0;
}

TcpClient.cc 服务端实现 

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

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

    // 创建套接字对象
    Net_Work::Socket *s = new Net_Work::TcpSocket();

    if (!s->BuildConnectSocket(serverip, serverport))
    {
        cerr << "connect [" << serverip << ":" << serverport << "] failed" << endl;
        return Net_Work::ConnectError;
    }
    cout << "connect [" << serverip << ":" << serverport << "] success" << endl;

    // 创建工厂
    unique_ptr<Protocol::Factory> factory(new Protocol::Factory());

    srand(time(nullptr) ^ getpid());
    const string opers = "+-*/%^&=";
    while (true)
    {
        // 1.构建一个请求
        int x = rand() % 100;
        usleep(rand() % 5555);
        int y = rand() % 100;
        int oper = opers[rand() % opers.size()];
        shared_ptr<Protocol::Request> req = factory->BuildRequest(x, y, oper);

        // 2.序列化该请求
        string requeststr;
        req->Serialize(&requeststr);

        // 3.添加报头信息
        requeststr = Protocol::Encode(requeststr);

        // 4.发送请求
        s->Send(requeststr);

        string message;
        while (true)
        {
            // 5.读取响应
            string responsestr;
            s->Recv(&responsestr, 1024);

            // 6.解析报文  收到的"len\n result code"  现在开始解析
            if(!Protocol::Decode(responsestr,&message))
                continue;

            // 7.拆分数据 目前是"result code"  现在开始写入结构体
            shared_ptr<Protocol::Response> resp = factory->BuildResponse();
            resp->Deserialize(message);

            // 8.resp已经得到的数据
            cout<<req->To_string()<<resp->Getresult()<<"\tcode: "<<resp->Getcode()<<endl;
            break;
        }
        sleep(1);
    }

    s->CloseSocket();
    return 0;
}

 Makefile

.PHONY:all
all:tcpserver tcpclient

tcpserver:Main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
tcpclient:TcpClient.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f tcpclient tcpserver

代码链接

运行结果吐下

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

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

相关文章

【Linux】进程间通信方式之管道

&#x1f916;个人主页&#xff1a;晚风相伴-CSDN博客 &#x1f496;如果觉得内容对你有帮助的话&#xff0c;还请给博主一键三连&#xff08;点赞&#x1f49c;、收藏&#x1f9e1;、关注&#x1f49a;&#xff09;吧 &#x1f64f;如果内容有误的话&#xff0c;还望指出&…

阿里云开发uniapp之uni-starter

一、为什么使用uni-starter uni-starter是集成商用项目常见功能的、云端一体应用快速开发项目模版。 一个应用有很多通用的功能&#xff0c;比如登录注册、个人中心、设置、权限管理、拦截器、banner... uni-starter将这些功能都已经集成好&#xff0c;另外&#xff0c;uni-s…

Linux下的SPI通信

SPI通信 一. 1.SPI简介: SPI 是一种高速,全双工,同步串行总线。 SPI 有主从俩种模式通常由一个主设备和一个或者多个从设备组从。SPI不支持多主机。 SPI通信至少需要四根线,分别是 MISO(主设备数据输入,从设备输出),MOSI (主设数据输出从设备输入),SCLK(时钟信号),CS/SS…

ARM(4)缓存一致性

目录 一、缓存一致性问题 二、一致性实现方案 2.1 目录一致性协议 2.2 嗅探一致性协议 三、CHI协议 3.1 cache state 3.2 snoop维护一致性 四、其他一致性协议 4.1 MSI协议 4.2 MESI 协议 4.3 MOESI协议 本文介绍以下内容&#xff1a; 缓存一致性问题一致性实现方案…

vCenter 7.3证书过期无法登录处理方法

登录报错&#xff1a;如下图 Exception in invking authentication handler [SSL: CERTIFICATE_VERIFY_FAILED] certificate vertify failed: certificate has expired(_ssl.c:1076) 处理方法1&#xff1a;推荐&#xff0c;可行 登录vCenter控制台&#xff0c;AltF3切换至命令…

【DFT】高 K/金属栅极阈值电压偏移的密度泛函模型

文章《Density functional model of threshold voltage shifts at High-K/Metal gates》&#xff0c;是由R. Cao、Z. Zhang、Y. Guo、J. Robertson等人撰写&#xff0c;发表在《Solid-State Electronics》期刊上。通过密度泛函理论&#xff08;Density Functional Theory, DFT&…

....comic科学....食用手册....

1.点击链接后&#xff0c;保存漫画至夸克网盘&#xff0c;若是新用户需要用手机注册. 2.在应用商店下载夸克APP. 3.登录APP下载已保存的漫画. 3.1 进入APP点击 夸克网盘 3.2 点击“转存的内容”后&#xff0c;长按 漫画文件夹&#xff0c;点击下载&#xff0c;下载速度400K左…

树(数据结构)

树的定义 一个根结点&#xff0c;其余结点分为 m 个不相交的集合&#xff0c; 其中每个集合本身又是一棵树&#xff0c;并且称为根的子树。 树的根结点没有前驱&#xff0c;其他结点有且仅有一个前驱。 所有结点可以有0个或多个后继。 基本术语 结点的度 树的度 &#xff1a; 树…

【Linux】深浅睡眠状态超详解!!!

1.浅度睡眠状态【S】&#xff08;挂起&#xff09; ——S (sleeping)可中断睡眠状态 进程因等待某个条件&#xff08;如 I/O 完成、互斥锁释放或某个事件发生&#xff09;而无法继续执行。在这种情况下&#xff0c;进程会进入阻塞状态&#xff0c;在阻塞状态下&#xff0c;进程…

mac 本地使用docker 运行es,kibana

1.下载 m芯片一些版本不支持.踩过坑.翻看官网才知道只有部分镜像支持m芯片 https://hub.docker.com/添加链接描述 docker pull elasticsearch:7.17.21 docker pull kibana:7.17.21镜像已经下载下来了 2.创建文件映射-挂载 /Users/lin/dev/dockerMsg 其中lin是自己的用户名…

设计模式(2)——工厂方法模式

目录 1. 摘要 2. 需求案例(设计一个咖啡店的点餐系统) 2.1 咖啡父类及其子类 2.2 咖啡店类与咖啡类的关系 3. 普通方法实线咖啡店点餐系统 3.1 定义Coffee父类 3.2 定义美式咖啡类继承Coffee类 3.3 定义拿铁咖啡继承Coffee类 3.4 定义咖啡店类 3.5 编写测试类 4. 简…

大模型微调实战之强化学习 贝尔曼方程及价值函数(五)

大模型微调实战之强化学习 贝尔曼方程及价值函数&#xff08;五&#xff09; 现在&#xff0c; 看一下状态-动作值函数的示意图&#xff1a; 这个图表示假设首先采取一些行动(a)。因此&#xff0c;由于动作&#xff08;a&#xff09;&#xff0c;代理可能会被环境转换到这些状…

docker Harbor私有仓库部署管理

搭建本地私有仓库&#xff0c;但是本地私有仓库的管理和使用比较麻烦&#xff0c;这个原生的私有仓库并不好用&#xff0c;所以我们采用harbor私有仓库&#xff0c;也叫私服&#xff0c;更加人性化。 一、什么是Harbor Harbor是VWware 公司开源的企业级Docker Registry项…

ESP8266-01s刷入固件报SP8266 Chip efuse check error esp_check_mac_and_efuse

一、遇到的问题 使用ESP8266 固件烧录工具flash_download_tools_v3.6.8 烧录固件报错&#xff1a; 二、解决方法 使用espressif推出发基于python的底层烧写工具&#xff1a;esptool 安装方法&#xff1a;详见https://docs.espressif.com/projects/esptool/en/latest/esp32/ …

腾讯游戏海外扩张,增持芬兰游戏开发商股份持股比例增至14.8%

易采游戏网5月8日消息&#xff0c;近日腾讯再次出手&#xff0c;大幅增持了芬兰知名游戏开发商Remedy Entertainment的股份&#xff0c;持股比例猛增至14.8%。这一举动引起了业界和投资者的广泛关注。 据了解&#xff0c;腾讯此次增持是在2024年4月24日完成的。根据芬兰法律规…

Pandas高效化运算与时间序列处理

文章目录 第1关&#xff1a;字符串操作方法第2关&#xff1a;Pandas的日期与时间工具第3关&#xff1a;Pandas时间序列的高级应用 第1关&#xff1a;字符串操作方法 任务描述 本关任务&#xff1a;读取step1/bournemouth_venues.csv文件&#xff0c;获取Venue Name列&#xff…

【C++】string类的使用②(容量接口Capacity || 元素获取Element access)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f525;容量接口&#xff08;Capacity&#xff09;size和lengthcapacitymax_sizereserveresizeclearemptyshrink_to_fit &#x1f525;元素获取&#xff08;Ele…

接口测试及常用的接口测试工具(Postman/Jmeter)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接…

phpstudy靶场访问显示404 Not Found

涉及靶场 upload-labd sqli-labs pikachu dvwa 以及所有部署在phpstudy中的靶场 一、检查phpstduy设置 localhost——管理——修改 1、根目录&#xff08;默认设置&#xff0c;不要改&#xff09; localhost这个域名必须保留&#xff0c;并且把根目录设置为phpstudy的WWW文…

Duplicate entry ‘asdfg‘ for key ‘clazz.name‘

Mybatis:java.sql.SQLIntegrityConstraintViolationException:Duplicate entry ‘asdfg’ for key ‘clazz.name’ 违反了数据库的唯一约束条件&#xff0c;即插入数据的时候具有唯一约束&#xff08;被unique修饰&#xff09;的列值重复了 在修改的过程中发生错误&#xff0c;…