网络计算器的实现:TCP、守护进程、Json、序列化与反序列化

发布于:2024-12-23 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

核心简介

代码实现网络计算器

Tcpserver.hpp

代码剖析

ServerCal.hpp

代码剖析

SeverCal.cc

代码剖析

protocal.hpp

代码剖析

ClientCal.cc

代码剖析

testjson.cc

Json介绍

其他注意点

重谈OSI的七层模型


核心简介

在当今信息化时代,网络计算器作为一种便捷的在线工具,越来越受到人们的青睐。它不仅能够实现传统计算器的功能,还能通过网络进行远程计算,为用户提供更加灵活和高效的服务。要实现一个网络计算器,我们需要掌握以下几个关键技术:

  1. TCP协议:传输控制协议(TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在网络计算器中,TCP协议负责确保数据在网络中的可靠传输,保证计算结果的准确性。通过TCP协议,客户端和服务器之间可以建立稳定的连接,实现数据的双向传输。

  2. 守护进程:守护进程是一种在后台运行的服务进程,它通常在系统启动时启动,并在系统关闭时终止。在网络计算器中,守护进程可以用来监听端口、处理客户端请求、维护计算器服务的持续运行。守护进程的稳定性保证了网络计算器的可用性。

  3. Json格式:Json(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。在网络计算器中,Json格式可以用来序列化客户端和服务器之间的数据,实现数据的标准化传输。Json格式的通用性使得网络计算器可以轻松与其他系统进行集成。

  4. 序列化与反序列化:序列化是将数据结构或对象状态转换为可存储或可传输的格式的过程,而反序列化则是将这种格式恢复为原始数据结构或对象的过程。在网络计算器中,序列化与反序列化技术用于将计算请求和结果在网络中传输,确保数据的完整性和可识别性。

  5. write与read只是用户到内核缓冲器的内容拷贝,真正的发送控制是tcp控制的。我们将制定一个协议,来保证接受与发送的数据是稳定可靠的。

通过以上技术的综合运用,我们可以构建一个稳定、高效、易于扩展的网络计算器,为用户提供便捷的在线计算服务。在接下来的内容中,我们将详细介绍这些技术的实现方法和应用场景。

代码实现网络计算器

Tcpserver.hpp

#pragma once

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

using namespace std;

using func_t = function<string(string &data)>;

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t callback)
        : _port(port), _callback(callback)
    {
    }

    bool InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        lg(Info, "init server .... done");
        return true;
    }

    void Start()
    {
        signal(SIGCHLD, SIG_IGN); // 调用系统调用daemon时,仍然需要手动忽略信号
        signal(SIGPIPE, SIG_IGN);
        signal(SIGINT, SIG_IGN);

        while (1)
        {
            std::string clientip;
            uint16_t clientport;

            int accsock = _listensock.Accept(&clientip, &clientport);
            if (accsock < 0)
                continue;
            lg(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", accsock, clientip.c_str(), clientport);

            // 提供服务
            std::string inbuffer_stream; // 接收到的流数据
            while (1)
            {
                char buffer[1280];
                ssize_t n = recv(accsock, buffer, sizeof(buffer), 0);

                if (n > 0)
                {
                    buffer[n] = '\0';
                    inbuffer_stream += buffer;

                    lg(Debug, "debug:\n%s", inbuffer_stream.c_str());

                    while (true)
                    {
                        std::string info = _callback(inbuffer_stream);  //可能接收到了多个数据包,需要分割处理
                        if (info.empty())
                            break;
                        lg(Debug, "debug, response:\n%s", info.c_str());
                        lg(Debug, "debug:\n%s", inbuffer_stream.c_str());
                        write(accsock, info.c_str(), info.size());
                    }
                }
                else if (n == 0)    
                    break;
                else
                    break;
            }

            close(accsock);
        }
    }

private:
    uint16_t _port;
    Sock _listensock;
    func_t _callback;
};

代码剖析

这段代码实现了一个简单的TCP服务器,它能够接受客户端连接并处理数据。以下是对代码的详细剖析:

### 头文件和命名空间
```cpp
#pragma once

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

using namespace std;
```
- `#pragma once`:防止头文件被多次包含。
- `#include <functional>`:引入标准库中的函数对象支持。
- `#include <string>`:引入字符串类。
- `#include <signal.h>`:引入信号处理相关函数。
- `#include "Log.hpp"`:自定义日志记录模块。
- `#include "Socket.hpp"`:自定义套接字操作模块。
- `using namespace std;`:使用标准命名空间。

### 类型定义
```cpp
using func_t = function<string(string &data)>;
```
- 定义了一个函数类型 `func_t`,它是一个接受 `string&` 参数并返回 `string` 的函数对象。

### TcpServer 类
#### 构造函数
```cpp
class TcpServer
{
public:
    TcpServer(uint16_t port, func_t callback)
        : _port(port), _callback(callback)
    {
    }
```
- 构造函数初始化端口号 `_port` 和回调函数 `_callback`。

#### InitServer 方法
```cpp
bool InitServer()
{
    _listensock.Socket();
    _listensock.Bind(_port);
    _listensock.Listen();
    lg(Info, "init server .... done");
    return true;
}
```
- 创建套接字、绑定端口并开始监听。
- 使用 `lg` 函数记录日志信息。

#### Start 方法
```cpp
void Start()
{
    signal(SIGCHLD, SIG_IGN); // 调用系统调用daemon时,仍然需要手动忽略信号
    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, SIG_IGN);

    while (1)
    {
        std::string clientip;
        uint16_t clientport;

        int accsock = _listensock.Accept(&clientip, &clientport);
        if (accsock < 0)
            continue;
        lg(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", accsock, clientip.c_str(), clientport);

        // 提供服务
        std::string inbuffer_stream; // 接收到的流数据
        while (1)
        {
            char buffer[1280];
            ssize_t n = recv(accsock, buffer, sizeof(buffer), 0);

            if (n > 0)
            {
                buffer[n] = '\0';
                inbuffer_stream += buffer;

                lg(Debug, "debug:\n%s", inbuffer_stream.c_str());

                while (true)
                {
                    std::string info = _callback(inbuffer_stream);  //可能接收到了多个数据包,需要分割处理
                    if (info.empty())
                        break;
                    lg(Debug, "debug, response:\n%s", info.c_str());
                    lg(Debug, "debug:\n%s", inbuffer_stream.c_str());
                    write(accsock, info.c_str(), info.size());
                }
            }
            else if (n == 0)    
                break;
            else
                break;
        }

        close(accsock);
    }
}
```
- 设置信号处理函数以忽略特定信号(如子进程终止信号、管道破裂信号和中断信号)。
- 进入无限循环,等待并接受客户端连接。
- 当有新的连接时,记录日志并处理该连接的数据。
- 使用 `recv` 函数从客户端读取数据,并将其追加到 `inbuffer_stream` 中。
- 调用回调函数 `_callback` 处理接收到的数据,并将结果发送回客户端。
- 关闭客户端连接。

### 私有成员变量
```cpp
private:
    uint16_t _port;
    Sock _listensock;
    func_t _callback;
};
```
- `_port`:服务器监听的端口号。
- `_listensock`:用于监听连接的套接字对象。
- `_callback`:处理数据的回调函数。

ServerCal.hpp

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

using namespace std;

enum
{
    Div_Zero = 1,
    Mod_Zero,
    Other_Oper
};


class ServerCal
{
private:
    // 计算器的核心算法
    Response CalculateHelper(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;      //break可以放在{}外面,表示一个代码块的结束
        
        case '%':
        {
            if (req.y == 0)
                resp.code = Mod_Zero;
            else
                resp.result = req.x % req.y;
        }
        break;
        
        default:
            resp.code = Other_Oper;
            break;
        }

        return resp;
    }


public:
    // "len"\n"10 + 20"\n
    std::string Calculator(std::string &package)
    {
        string content;
        
        bool r = Decode(package, &content);
        if (!r) return "Decode Error";

        //"10 + 20"
        Request req;
        r = req.Deserialize(content);   //"10 + 20" ->x=10 op=+ y=20,存放在了req中
        content.clear();

        Response rsp;
        rsp = CalculateHelper(req);     // result=30 code=0;

        r = rsp.Serialize(&content);     // "30 0"
        if (!r) return "Serialize Error";

        Encode(content);     // "len"\n"30 0"

        return content;
    }

};

代码剖析

这段代码实现了一个简单的服务器端计算器,它能够处理基本的算术运算(加、减、乘、除和取模)。以下是对代码的详细剖析:

### 头文件和命名空间
```cpp
#pragma once
#include <iostream>
#include "Protocol.hpp"

using namespace std;
```
- `#pragma once`:防止头文件被多次包含。
- `#include <iostream>`:引入标准输入输出流库。
- `#include "Protocol.hpp"`:引入自定义的协议头文件,假设这个文件中定义了 `Request` 和 `Response` 类以及相关的序列化和反序列化方法。
- `using namespace std;`:使用标准命名空间,避免每次使用标准库对象时都需要加上 `std::` 前缀。

### 枚举类型
```cpp
enum
{
    Div_Zero = 1,
    Mod_Zero,
    Other_Oper
};
```
- 定义了一些错误码,用于表示不同的错误情况:
  - `Div_Zero`:除零错误。
  - `Mod_Zero`:取模零错误。
  - `Other_Oper`:其他操作符错误。

### ServerCal 类
#### 私有成员函数
```cpp
class ServerCal
{
private:
    // 计算器的核心算法
    Response CalculateHelper(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;      //break可以放在{}外面,表示一个代码块的结束
        
        case '%':
        {
            if (req.y == 0)
                resp.code = Mod_Zero;
            else
                resp.result = req.x % req.y;
        }
        break;
        
        default:
            resp.code = Other_Oper;
            break;
        }

        return resp;
    }
```
- `CalculateHelper` 是一个私有成员函数,用于根据请求中的操作符执行相应的计算。如果遇到除零或取模零的情况,会设置相应的错误码。

#### 公有成员函数
```cpp
public:
    // "len"\n"10 + 20"\n
    std::string Calculator(std::string &package)
    {
        string content;
        
        bool r = Decode(package, &content);
        if (!r) return "Decode Error";

        //"10 + 20"
        Request req;
        r = req.Deserialize(content);   //"10 + 20" ->x=10 op=+ y=20,存放在了req中
        content.clear();

        Response rsp;
        rsp = CalculateHelper(req);     // result=30 code=0;

        r = rsp.Serialize(&content);     // "30 0"
        if (!r) return "Serialize Error";

        Encode(content);     // "len"\n"30 0"

        return content;
    }
};
```
- `Calculator` 是公有成员函数,负责处理整个计算过程。具体步骤如下:
  1. **解码**:调用 `Decode` 函数将输入的字符串解码成内容字符串。如果解码失败,返回 "Decode Error"。
  2. **反序列化**:调用 `Request` 对象的 `Deserialize` 方法将内容字符串反序列化为 `Request` 对象。如果反序列化失败,返回 "Decode Error"。
  3. **计算**:调用 `CalculateHelper` 方法进行计算,得到 `Response` 对象。
  4. **序列化**:调用 `Response` 对象的 `Serialize` 方法将结果序列化为字符串。如果序列化失败,返回 "Serialize Error"。
  5. **编码**:调用 `Encode` 函数将序列化后的字符串编码成最终的输出格式。
  6. **返回结果**:返回编码后的字符串。

### 总结
这段代码实现了一个简单的服务器端计算器,通过解析客户端发送的请求,进行相应的计算并返回结果。代码结构清晰,使用了面向对象的设计思想,并且通过枚举类型来处理各种错误情况。

SeverCal.cc

#include "TcpServer.hpp"
#include "ServerCal.hpp"
#include <unistd.h>

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

// ./servercal 8080
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(port, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));
    tsvp->InitServer();
    // Daemon();

    //守护进程
    daemon(0, 0);
    tsvp->Start();
    return 0;
}

代码剖析

#include "TcpServer.hpp"
#include "ServerCal.hpp"
#include <unistd.h>

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

// ./servercal 8080
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(port, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));
    tsvp->InitServer();
    // Daemon();

    //守护进程
    daemon(0, 0);
    tsvp->Start();
    return 0;
}

protocal.hpp

#pragma once

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

using namespace std;

// #define MySelf 1
// #define MySelf=1

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

// response和request都需要使用decode和encode

// "len"\n"x op y"\nXXXXXX
// "protocolnumber"\n"len"\n"x op y"\nXXXXXX

// content-->package
string Encode(string &content)
{
    string package = to_string(content.size());

    package += protocol_sep;
    package += content;
    package += protocol_sep;

    return package;
}

// package-->content
bool Decode(string &package, string *content)
{
    size_t pos = package.find(protocol_sep);
    if (pos == std::string::npos)
        return false;

    std::string len_str = package.substr(0, pos); // 左闭右开
    std::size_t len = std::stoi(len_str);
    size_t total_len = len + len_str.size() + 2;
    if (total_len > package.size())
        return false;

    // 获取内容与移除已经获取的内容
    *content = package.substr(pos + 1, len);
    package.erase(0, total_len);
    return true;
}

// 序列化的最常用的方案:json, protobuf

class Request
{
public:
    Request(int x, int y, char op) : x(x), y(y), op(op) {}
    Request() {} // 默认构造函数

public:
    // 序列化"x op y"
    bool Serialize(string *out)
    {
#ifdef MySelf
        Json::Value root;
        root["x"] = x;
        root["y"] = y;
        root["op"] = op;
        // Json::FastWriter w;
        Json::StyledWriter w;
        *out = w.write(root);
#else
        string content;
        content += to_string(x) + blank_space_sep + op + blank_space_sep + to_string(y);

        *out = content;
#endif
        return true;
    }

    // 反序列化"x op y"
    bool Deserialize(string &in)    //拆分 + 安全检查
    {

#ifdef MySelf
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);

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

#else
        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);
#endif
        return true;
    }
    
    void DebugPrint()
    {
        std::cout << "新请求构建完成:  " << x << op << y << "=?" << std::endl;
    }

public:        
        // x op y
        int x;
        int y;
        char op; // + - * / %
};


class Response
{
public:
    Response(int result, int code) : result(result), code(code) {}
    Response() {} // 默认构造函数

public:
    // 序列化"result code"
    bool Serialize(string *out)
    {
        // "result code"
        // 构建报文的有效载荷
        std::string s = std::to_string(result);
        s += blank_space_sep;
        s += std::to_string(code);
        *out = s;
        return true;
    }

    bool Deserialize(string &in)    //拆分 + 安全检查
    {
        std::size_t pos = in.find(blank_space_sep);
        if (pos == std::string::npos)
            return false;
        std::string part_left = in.substr(0, pos);
        std::string part_right = in.substr(pos+1);

        result = std::stoi(part_left);
        code = std::stoi(part_right);

        return true;
    }
  
    void DebugPrint()
    {
        std::cout << "结果响应完成, result: " << result << ", code: "<< code << std::endl;
    }
public:
    int result;
    int code; // 0,可信,否则!0具体是几,表明对应的错误原因
};

代码剖析

这段代码实现了一个简单的请求-响应协议,其中包含对请求和响应的序列化和反序列化功能。以下是对代码的详细剖析:

### 头文件和命名空间
```cpp
#pragma once

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

using namespace std;
```
- `#pragma once`:防止头文件被多次包含。
- 包含了标准输入输出流、字符串处理和JSON库的头文件。
- 使用`std`命名空间。

### 常量定义
```cpp
const std::string blank_space_sep = " ";
const std::string protocol_sep = "\n";
```
- 定义了两个常量字符串,分别用于分隔符和协议分隔符。

### 编码和解码函数
```cpp
// content-->package
string Encode(string &content)
{
    string package = to_string(content.size());
    package += protocol_sep;
    package += content;
    package += protocol_sep;
    return package;
}

// package-->content
bool Decode(string &package, string *content)
{
    size_t pos = package.find(protocol_sep);
    if (pos == std::string::npos)
        return false;

    std::string len_str = package.substr(0, pos); // 左闭右开
    std::size_t len = std::stoi(len_str);
    size_t total_len = len + len_str.size() + 2;
    if (total_len > package.size())
        return false;

    *content = package.substr(pos + 1, len);
    package.erase(0, total_len);
    return true;
}
```
- `Encode`函数将内容编码为一个带有长度信息的包。
- `Decode`函数从包中提取内容,并更新包以移除已提取的内容。

### Request类
```cpp
class Request
{
public:
    Request(int x, int y, char op) : x(x), y(y), op(op) {}
    Request() {} // 默认构造函数

public:
    bool Serialize(string *out)
    {
#ifdef MySelf
        Json::Value root;
        root["x"] = x;
        root["y"] = y;
        root["op"] = op;
        Json::StyledWriter w;
        *out = w.write(root);
#else
        string content;
        content += to_string(x) + blank_space_sep + op + blank_space_sep + to_string(y);
        *out = content;
#endif
        return true;
    }

    bool Deserialize(string &in)
    {
#ifdef MySelf
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);
        x = root["x"].asInt();
        y = root["y"].asInt();
        op = root["op"].asInt();
#else
        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);
#endif
        return true;
    }
    
    void DebugPrint()
    {
        std::cout << "新请求构建完成:  " << x << op << y << "=?" << std::endl;
    }

public:        
    int x;
    int y;
    char op; // + - * / %
};
```
- `Request`类表示一个计算请求,包含三个成员变量:`x`, `y`和`op`。
- `Serialize`方法将请求对象序列化为字符串。如果定义了`MySelf`宏,则使用JSON格式,否则使用空格分隔的字符串格式。
- `Deserialize`方法从字符串中反序列化出请求对象。同样地,根据是否定义了`MySelf`宏选择不同的解析方式。
- `DebugPrint`方法用于调试,打印请求内容。

### Response类
```cpp
class Response
{
public:
    Response(int result, int code) : result(result), code(code) {}
    Response() {} // 默认构造函数

public:
    bool Serialize(string *out)
    {
        std::string s = std::to_string(result);
        s += blank_space_sep;
        s += std::to_string(code);
        *out = s;
        return true;
    }

    bool Deserialize(string &in)
    {
        std::size_t pos = in.find(blank_space_sep);
        if (pos == std::string::npos)
            return false;
        std::string part_left = in.substr(0, pos);
        std::string part_right = in.substr(pos+1);
        result = std::stoi(part_left);
        code = std::stoi(part_right);
        return true;
    }
  
    void DebugPrint()
    {
        std::cout << "结果响应完成, result: " << result << ", code: "<< code << std::endl;
    }
public:
    int result;
    int code; // 0,可信,否则!0具体是几,表明对应的错误原因
};
```
- `Response`类表示一个计算响应,包含两个成员变量:`result`和`code`。
- `Serialize`方法将响应对象序列化为字符串,使用空格分隔。
- `Deserialize`方法从字符串中反序列化出响应对象。
- `DebugPrint`方法用于调试,打印响应内容。

### 总结
这段代码实现了一个简单的请求-响应协议,支持对请求和响应对象的序列化和反序列化操作。通过条件编译可以选择不同的序列化方式(JSON或空格分隔字符串)。

ClientCal.cc

代码剖析

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

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

// ./clientcal ip port

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 <= 10)
    {
        std::cout << "===============第" << cnt << "次测试....., " << "===============" << std::endl;
        int x = rand() % 100 + 1;
        usleep(1234);
        int y = rand() % 100;
        usleep(4321);
        char oper = opers[rand()%opers.size()];
        Request req(x, y, oper);
        req.DebugPrint();

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

        package = Encode(package);

        write(sockfd.GetFd(), package.c_str(), package.size());
        // std::cout << "这是最新的发出去的请求: " << n << "\n" << package;
        // n = write(sockfd.Fd(), package.c_str(), package.size());
        // std::cout << "这是最新的发出去的请求: \n" << n << "\n" << package;
        // n = write(sockfd.Fd(), package.c_str(), package.size());
        // std::cout << "这是最新的发出去的请求: \n" << n << "\n" << package;
        // n = write(sockfd.Fd(), package.c_str(), package.size());
        // std::cout << "这是最新的发出去的请求: \n" << n << "\n" << package;


        char buffer[128];
        ssize_t n = read(sockfd.GetFd(), buffer, sizeof(buffer)); // 我们也无法保证我们能读到一个完整的报文
        if(n > 0)
        {
            buffer[n] = 0;
            inbuffer_stream += buffer; // "len"\n"result code"\n
            std::cout << inbuffer_stream << std::endl;
            std::string content;
            bool r = Decode(inbuffer_stream, &content); // "result code"
            assert(r);

            Response resp;
            r = resp.Deserialize(content);
            assert(r);

            resp.DebugPrint();
        }

        std::cout << "=================================================" << std::endl;
        sleep(1);

        cnt++;
    }

    return 0;
}

testjson.cc

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

using namespace std;

// Json命名空间中包含:Value Read StyledWriter FastWriter
//Value对象用来构建json对象(键值对风格的字符串)
//FastWriter对象用来输出json对象,StyledWriter对象用来输出风格化的json对象
//Reader对象的parse方法,传入一个string对象和Value对象,可以解析json字符串并构建Value对象
//用int x = v["x"].asInt();可以获取json对象中的x的值,asInt()方法用来获取整形值



int main() 
{
    //构建KV的json对象
    Json::Value part1;
    part1["haha"] = "haha";
    part1["hehe"] = "hehe";


    Json::Value root;       //Json是命名空间,Value是类名
    root["x"] = 100;
    root["y"] = 200;
    root["op"] = '+';
    root["desc"] = "this is a + oper";
    root["test"] = part1;

    //输出json对象
    Json::StyledWriter w; 
    string json_str = w.write(root);
    cout << json_str << endl;


    //解析json对象
    Json::Reader r;
    Json::Value v;
    //解析的数据放到v中
    r.parse(json_str, v);

    int x = v["x"].asInt();
    char op = v["op"].asInt();      //字符也是整形家族
    string desc = v["desc"].asString();
    Json::Value test = v["test"];
    string haha = test["haha"].asString();

    cout << "x: " << x << endl;
    cout << "op: " << op << endl;
    cout << "desc: " << desc << endl;
    cout << "haha: " << haha << endl;

    return 0;
}

Json命名空间中包含:Value Read StyledWriter FastWriter

Value对象用来构建json对象(键值对风格的字符串)

FastWriter对象用来输出json对象,StyledWriter对象用来输出风格化的json对象

Reader对象的parse方法,传入一个string对象和Value对象,可以解析json字符串并构建Value对象

用int x = v["x"].asInt();可以获取json对象中的x的值,asInt()方法用来获取整形值

Json介绍

包含头文件的时候,只需要jsoncpp/json/XXXX.h

在编译的时候,需要-ljsoncpp

需要注意的是,序列化与反序列化可以交给json,但是报头需要手动添加。

其他注意点

1.系统提供的daemon

2.函数名本身就是函数指针,但是如果是类内函数的话,需要 &类域::函数名 的操作去获得函数指针

3.recv和recvfrom的最后一个参数

4.为什么sendto(UDP)需要传入sockaddr结构,但是send(TCP)不需要

`sendto` 和 `send` 函数在 UDP 和 TCP 协议中的使用差异,反映了这两种协议的本质区别:

1. **UDP(用户数据报协议)**:

   - UDP 是无连接的协议,它不维护持续的网络连接状态。

   - 每次发送数据时,都需要明确指定数据的目的地址,因为UDP不会记住之前的数据传输信息。

   - 因此,`sendto` 函数需要传入一个 `sockaddr` 结构,这个结构包含了目标IP地址和端口号,以便内核知道数据应该发送到哪里。

   `sendto` 函数的原型如下:

   ```c

   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,

                  const struct sockaddr *dest_addr, socklen_t addrlen);

   ```

2. **TCP(传输控制协议)**:

   - TCP 是面向连接的协议,它在数据传输之前需要建立一个连接。

   - 一旦建立了连接,TCP 会维护一个稳定的端到端连接状态,直到连接被显式关闭。

   - 在连接建立后,使用 `send` 函数发送数据时,不需要再次指定目标地址,因为连接已经确定了数据的发送方向。

   - `send` 函数只需要知道要发送的数据、数据长度和可能的标志。

   `send` 函数的原型如下:

   ```c

   ssize_t send(int sockfd, const void *buf, size_t len, int flags);

   ```

总结来说,`sendto` 需要传入 `sockaddr` 结构是因为 UDP 协议本身不维护连接状态,而 `send` 不需要是因为 TCP 已经在连接建立时确定了通信双方,因此在发送数据时不需要重复指定目标地址。

因此对于TCP连接而言,只要accept获取到了套接字,维持了连接,那么直接往accsock写入信息,对方就可以直接recv

重谈OSI的七层模型

应用层:计算器的计算原理

表示层:json协议……

会话层:Tcpsever.hpp

通信管理获取新连接,通过子进程维护新连接的使用状况,子进程关闭那就无法继续使用,子进程不关闭,那就继续使用

创建子进程来提供服务,就相当于,你每次放问我,我都给你新建了一个会话,由子进程处理新的连接,不需要的时候,把新连接关掉。