• http协议(序列化与反序列化)


    1.序列化与反序列化的过程

    如果客户端的数据是一个结构体,这个结构体要发送给服务端。在网络传输的过程中需要将这个结构体转化成字符串,这个过程称为序列化。而数据由字符串转化成结构体再被服务端接收的过程,称为反序列化过程。
    结构化的过程其实就是一种协议的体现
    在这里插入图片描述

    2.序列化的原因

    1.结构化的数据不利于网络的传输(为了应用层网络通信的方便)。
    2.为了方便上层使用内部成员,将应用层和网络进行了解耦。
    我们之前做的tcp和UDP通信没有做任何序列化与反序列化。可以json等组件来实现序列化与反序列化。

    3.json实现序列化

    3.1安装json

    yum install -y jsoncpp-devel

    安装后我们可以在/usr/include/jsoncpp/json找到安装的头文件。
    json是一款实现序列化和反序列化的组件。假设我们要实现一个网络版的计算器,客户端发送与服务端发送的结构体如下:

    #include 
    using namespace std;
    typedef struct request
    {
        int x;
        int y;
        char op;
    } request_t;
    typedef struct response
    {
        int code; // code(0)表示成功,code(-1)表示除以0,code(-2)表示对0取模
        int result;
    } response_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.2json的使用

    3.2.1序列化使用

    1.首先建立Value对象,使用其来接收结构体中的值。
    2.使用Writer对象的write方法,将root对象写入字符串中,即可形成一个序列化的字符串。形成的序列化字符串有我们新增的key的内容,在反序列化的时候可以拿掉。

        request_t req={10,20,'*'};
        Json::Value root;//可以承载任何对象,json是一种KV式的序列化方案
        root["datax"]=req.x;
        root["datay"]=req.y;
        root["operator"]=req.op;
        Json::StyledWriter writer;
        string json_string=writer.write(root);
        cout<<json_string<<endl;
        return 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    拿到的字符串是:
    在这里插入图片描述

    3.2.2反序列化的过程

    1.首先建立Value对象与Reader对象。
    2.使用Reader对象的parse方法,将Value对象与字符串相关联。
    3.使用Value对象的KV结构来对结构体赋值。

        string json_string=R"({"datax":10,"datay":20,"operator":42})";
        cout<<json_string<<endl;
        Json::Value root;
        Json::Reader reader;
        reader.parse(json_string,root);//将root对象与要反序列化的字符串关联
        request_t req;
        req.x=root["datax"].asInt();
        req.y=root["datay"].asInt();
        req.op=(char)root["operator"].asInt();
        cout<<req.x<<req.y<<req.op<<endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.实现网络版本的计算器

    3.1指定协议与序列化反序列化

    制定协议的过程,其实就是规定服务端与客户端的结构化数据的结构是什么,比如,我们规定客户端发送的数据的格式和服务端接收数据的格式:

    #include 
    #include
    using namespace std;
    typedef struct request
    {
        int x;
        int y;
        char op;
    } request_t;
    typedef struct response
    {
        int code; // code(0)表示成功,code(-1)表示除以0,code(-2)表示对0取模
        int result;
    } response_t;
    string SerializeRequest(const request_t& req)
    {
        Json::Value root;
        Json::FastWriter writer;
        root["datax"]=req.x;
        root["datay"]=req.y;
        root["operator"]=req.op;
        string json_string=writer.write(root);
        return json_string;
    }
    void DeserializeRequest(string& json,request_t& out)
    {
        Json::Value root;
        Json::Reader reader;
        reader.parse(json,root);
        out.x=root["datax"].asInt();
        out.y=root["datay"].asInt();
        out.op=(char)root["operator"].asInt();
    }
    string SerializeResponse(const response& res)
    {
        Json::Value root;
        Json::FastWriter writer;
        root["code"]=res.code;
        root["result"]=res.result;
        string json_string=writer.write(root);
        return json_string;
    }
    void DeserializeResponse(string& json,response_t& out)
    {
        Json::Value root;
        Json::Reader reader;
        reader.parse(json,root);
        out.code=root["code"].asInt();
        out.result=root["result"].asInt();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    3.2基本网络通信(采用TCP协议)

    #include 
    #include
    #include
    #include
    #include
    #include
    #include
    using namespace std;
    namespace ns_Sock
    {
        class Sock
        {
        private:
        public:
            static int Socket()
            {
                int sock = socket(AF_INET, SOCK_STREAM, 0);
                if (sock < 0)
                {
                    cerr << "创建套接字失败" << endl;
                    exit(-2);
                }
                else
                {
                    return sock;
                }
            }
            static int Listen(int sock)
            {
                if(listen(sock,5)<0)
                {
                    cerr<<"listen error"<<endl;
                    exit(-3);
                }
            }
            static int Accept(int sock)
            {
                struct sockaddr_in peer;
                socklen_t len=sizeof(peer);
                int fd=accept(sock,(struct sockaddr*)&peer,&len);
                if(fd>=0)
                {
                    return fd;
                }
                else
                {
                    exit(-5);
                }
            }
            static void Bind(int sock,uint16_t port)
            {
                sockaddr_in local;
                local.sin_family=AF_INET;
                local.sin_port=htons(port);
                local.sin_addr.s_addr=INADDR_ANY;
                if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
                {
                    cerr<<"bind error"<<endl;
                    exit(-4);
                }
            }
            static void Connect(int sock,string ip,uint16_t port)
            {
                struct sockaddr_in server;
                memset(&server,0,sizeof(server));
                server.sin_family=AF_INET;
                server.sin_port=htons(port);
                server.sin_addr.s_addr=inet_addr(ip.c_str());
                if(connect(sock,(struct sockaddr*)&server,sizeof(server))==0)
                {
                    cout<<"connect success!"<<endl;
                }
                else
                {
                    cout<<"connect fail"<<endl;
                    exit(-5);
                }
            }
    
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    3.3通信具体业务

    3.3.1服务端

    #include "protonol.hpp"
    #include "Sock.hpp"
    using namespace ns_Sock;
    using namespace std;
    void Usage(string proc)
    {
      cout << proc << " port" << endl;
    }
    void *HandlerRequest(void *args)
    {
      int sock = *(int *)args;
      delete (int *)args;
      pthread_detach(pthread_self());
      char buffer[1024];
      ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
      request_t req;
      response_t res = {0, 0};
      buffer[s] = 0;
      string str = buffer;
      cout << str << endl;
      DeserializeRequest(str, req);
      switch (req.op)
      {
      case '+':
        res.result = req.x + req.y;
        break;
      case '-':
        res.result = req.x - req.y;
        break;
      case '*':
        res.result = req.x * req.y;
        break;
      case '/':
        if (req.y == 0)
          res.code = -1;
        else
          res.result = req.x / req.y;
        break;
      case '%':
        if (req.y == 0)
          res.code = -2;
        else
          res.result = req.x % req.y;
        break;
      default:
        res.code = -3;
      }
      cout << "code=" << res.code << endl;
      cout << req.x << req.op << req.y << "=" << res.result << endl;
      string json_string = SerializeResponse(res);
      write(sock, json_string.c_str(), json_string.size());
      cout << "send an answer" << json_string;
      // write(sock,&res,sizeof(res));
    }
    
    int main(int argc, char *argv[])
    {
      if (argc != 2)
      {
        Usage(argv[0]);
        return -1;
      }
      uint16_t port = atoi(argv[1]);
      int listen_sock = Sock::Socket();
      Sock::Bind(listen_sock, port);
      Sock::Listen(listen_sock);
      while (true)
      {
        int sock = Sock::Accept(listen_sock);
        if (sock > 0)
        {
          cout << "get a new link..." << endl;
          int *pram = new int(sock);
          pthread_t tid;
          pthread_create(&tid, nullptr, HandlerRequest, pram);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    3.3.2客户端

    #include"Sock.hpp"
    #include"protonol.hpp"
    using namespace std;
    using namespace ns_Sock;
    void Usage(string proc)
    {
        cout<<proc<<" ip"<<" port"<<endl;
    }
    int main(int argc,char* argv[])
    {
        if(argc!=3)
        {
            Usage(argv[0]);
            return -1;
        }
        int sock=Sock::Socket();
        Sock::Connect(sock,argv[1],atoi(argv[2]));
        request_t req;
        memset(&req,0,sizeof(req));
        cout<<"Please Enter Data1:#";
        cin>>req.x;
        cout<<"Please Enter Data2:#";
        cin>>req.y;
        cout<<"Please Enter op:#";
        cin>>req.op;
        string json_string=SerializeRequest(req);
        cout<<json_string<<endl;
        write(sock,json_string.c_str(),json_string.size());
        response_t res;
        char buffer[1024];
        memset(buffer,0,sizeof(buffer));
        ssize_t s=read(sock,&buffer,sizeof(buffer)-1);
        buffer[s]=0;
        string str=buffer;
        cout<<str<<endl;
        DeserializeResponse(str,res);
        cout<<"code[0:-3]#"<<res.code<<endl;
        cout<<"result#"<<res.result<<endl;
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    4.总结

    我们可以将上面的代码分为三部分,分别是基本通信代码(会话层),序列化反序列化代码(表示层),约定以及业务逻辑代码(应用层)。这些在TCP/IP四层协议中统称为应用层。http协议是应用层协议,因此需要完成以上三个工作。

  • 相关阅读:
    ServletContext对象
    C专家编程 第5章 对链接的思考 5.1 函数库、链接和载入
    SpringCloud的服务发现框架 — Eureka入门
    spingboot按照依赖包除了maven还有Gradle,两者的区别?
    ABAP FB05 清账函数:POSTING_INTERFACE_CLEARING
    【原型设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
    Linux基础入门到精通之虚拟机网络设置说明
    数据湖和数据仓库的区别?
    linux 安装nginx
    从0到1学会Git(第三部分):Git的远程仓库链接与操作
  • 原文地址:https://blog.csdn.net/qq_51492202/article/details/126354327