You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用nlohmann JSON实现TCP套接字读写及JSON与原始数据互转?

使用nlohmann JSON库的两个核心操作:原始数据转JSON与TCP套接字读写

一、将原始数据转换为JSON

原始数据的形式不同,转换方式也有区别,常见场景如下:

1. 从字符串/字符数组解析JSON

如果原始数据是JSON格式的字符串或字符数组,直接通过构造函数或parse()方法即可完成转换:

#include <nlohmann/json.hpp>
#include <string>
#include <iostream>

using json = nlohmann::json;

int main() {
    // 原始字符数组数据
    const char* raw_cstr = "{ \"happy\": true, \"pi\": 3.141 }";
    json j1 = raw_cstr;
    
    // 原始字符串数据
    std::string raw_str = "{ \"name\": \"Alice\", \"age\": 25 }";
    json j2 = json::parse(raw_str);
    
    // 验证转换结果
    std::cout << j1["pi"] << std::endl;
    std::cout << j2["name"] << std::endl;
}

2. 自定义业务结构体转JSON

如果原始数据是自定义结构体,需要重载to_json函数,让nlohmann库识别结构体的序列化规则:

#include <nlohmann/json.hpp>
#include <string>
#include <iostream>

using json = nlohmann::json;

// 自定义原始业务数据结构体
struct User {
    std::string name;
    int age;
    bool is_vip;
};

// 重载to_json:将User结构体转为JSON对象
void to_json(json& j, const User& user) {
    j = json{
        {"name", user.name},
        {"age", user.age},
        {"is_vip", user.is_vip}
    };
}

// 可选:重载from_json,实现JSON转结构体(反向操作)
void from_json(const json& j, User& user) {
    j.at("name").get_to(user.name);
    j.at("age").get_to(user.age);
    j.at("is_vip").get_to(user.is_vip);
}

int main() {
    // 原始结构体数据
    User alice = {"Alice", 25, true};
    // 转换为JSON对象
    json j = alice;
    
    // 输出格式化后的JSON字符串
    std::cout << j.dump(4) << std::endl;
}

二、TCP套接字的JSON读写操作

TCP读写的核心逻辑是:发送时将JSON序列化为字符串,接收时将字符串解析为JSON,以下是基于POSIX套接字的示例(Windows平台需调整套接字API):

1. TCP客户端发送JSON数据

#include <nlohmann/json.hpp>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <iostream>

using json = nlohmann::json;

int main() {
    // 创建TCP套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket creation failed");
        return -1;
    }

    // 配置服务器地址
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8080);
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("invalid server address");
        close(sock);
        return -1;
    }

    // 连接服务器
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("connection failed");
        close(sock);
        return -1;
    }

    // 构造JSON数据
    json send_data = {
        {"type", "login"},
        {"user", "Bob"},
        {"timestamp", 1699999999}
    };
    // 序列化为字符串
    std::string send_str = send_data.dump();
    // 发送数据(生产环境需处理粘包/分包)
    ssize_t sent_bytes = send(sock, send_str.c_str(), send_str.size(), 0);
    if (sent_bytes < 0) {
        perror("send failed");
    } else {
        std::cout << "Sent " << sent_bytes << " bytes: " << send_str << std::endl;
    }

    close(sock);
    return 0;
}

2. TCP服务器接收并解析JSON数据

#include <nlohmann/json.hpp>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <cstring>

using json = nlohmann::json;

int main() {
    // 创建服务器套接字
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sock < 0) {
        perror("socket creation failed");
        return -1;
    }

    // 设置地址重用
    int opt = 1;
    if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt failed");
        close(server_sock);
        return -1;
    }

    // 配置服务器地址
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(8080);

    // 绑定地址
    if (bind(server_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("bind failed");
        close(server_sock);
        return -1;
    }

    // 监听连接
    if (listen(server_sock, 3) < 0) {
        perror("listen failed");
        close(server_sock);
        return -1;
    }

    std::cout << "Server listening on port 8080..." << std::endl;

    // 接受客户端连接
    struct sockaddr_in cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    int client_sock = accept(server_sock, (struct sockaddr*)&cli_addr, &cli_len);
    if (client_sock < 0) {
        perror("accept failed");
        close(server_sock);
        return -1;
    }

    // 接收数据(缓冲区大小按需调整)
    char buffer[1024] = {0};
    ssize_t recv_bytes = recv(client_sock, buffer, sizeof(buffer)-1, 0);
    if (recv_bytes < 0) {
        perror("recv failed");
    } else {
        std::cout << "Received " << recv_bytes << " bytes: " << buffer << std::endl;
        // 解析为JSON对象,捕获解析异常
        try {
            json recv_data = json::parse(buffer);
            std::cout << "Parsed JSON: " << std::endl;
            std::cout << recv_data.dump(4) << std::endl;
            // 访问JSON字段
            std::cout << "User: " << recv_data["user"] << std::endl;
        } catch (const json::parse_error& e) {
            std::cerr << "JSON parse error: " << e.what() << std::endl;
        }
    }

    close(client_sock);
    close(server_sock);
    return 0;
}

关键注意事项

  • 粘包/分包处理:实际TCP通信中,JSON数据可能被拆分或合并,需自定义协议(比如在JSON前添加数据长度前缀)保证完整解析。
  • 错误处理:示例仅做基础错误捕获,生产环境需完善套接字操作、JSON解析等场景的异常处理。

内容的提问来源于stack exchange,提问作者Zvi Vered

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 22:46:03