网络编程(10)——json序列化

news2024/11/16 19:40:58

十、day10

今天学习如何使用jsoncpp将json数据解析为c++对象,将c++对象序列化为json数据。jsoncp经常在网络通信中使用,也就是服务器和客户端的通信一般使用json(可视化好);而protobuf一般在服务器之间的通信中使用

jsoncpp下载地址:open-source-parsers/jsoncpp: A C++ library for interacting with JSON. (github.com)

jsoncpp如何配置使用可参考博主

恋恋风辰官方博客​llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2Q5XIMAjJ76n2snyNEHstog2W9b

1.如果从github上下载最新版本,cmake编译后使用jsoncpp库,博主恋恋风辰给出的示例代码会造成内存泄漏,应该是最新版的库内部做了一些调整,该问题我没有解决,使用的仍是旧版本的jsoncpp。

2.如果旧版本的jsoncpp没有x64平台,需要自己在管理那里添加设置,确保平台和线程使用的统一性。

3.客户端和服务器做如下调整:

客户端

客户端和服务器都需要包含库目录和包含目录,比如

且需要在链接器→输入→附加依赖项中,添加需要的库

libprotobufd.lib
libprotocd.lib
json_vc71_libmtd.lib

客户端代码做以下调整

#include <boost/asio.hpp>
#include <iostream>
#include <json/json.h>
#include <json/value.h>
#include <json/reader.h>

using namespace boost::asio::ip;
using std::cout;
using std::endl;
const int MAX_LENGTH = 1024 * 2; // 发送和接收的长度为1024 * 2字节
const int HEAD_LENGTH = 2;

int main()
{
    try {
        boost::asio::io_context ioc; // 创建上下文服务
        // 127.0.0.1是本机的回路地址,也就是服务器和客户端在一个机器上
        tcp::endpoint remote_ep(address::from_string("127.0.0.1"), 10086); // 构造endpoint
        tcp::socket sock(ioc);
        boost::system::error_code error = boost::asio::error::host_not_found; // 错误:主机未找到
        sock.connect(remote_ep, error);
        if (error) {
            cout << "connect failed, code is " << error.value() << " error msg is " << error.message();
            return 0;
        }

        Json::Value root;
        root["id"] = 1001;
        root["data"] = "hello world";
        std::string request = root.toStyledString();
        size_t request_length = request.length();
        char send_data[MAX_LENGTH] = { 0 };
        //转为网络字节序
        int request_host_length = boost::asio::detail::socket_ops::host_to_network_short(request_length);
        memcpy(send_data, &request_host_length, 2);
        memcpy(send_data + 2, request.c_str(), request_length);
        boost::asio::write(sock, boost::asio::buffer(send_data, request_length + 2));

        char reply_head[HEAD_LENGTH]; // 首先读取对端发送消息的总长度
        size_t reply_length = boost::asio::read(sock, boost::asio::buffer(reply_head, HEAD_LENGTH));
        short msglen = 0; // 消息总长度
        memcpy(&msglen, reply_head, HEAD_LENGTH); // 将消息总长度赋值给msglen
        //转为本地字节序
        msglen = boost::asio::detail::socket_ops::network_to_host_short(msglen);
        char msg[MAX_LENGTH] = { 0 }; // 构建消息体(不含消息总长度)
        size_t msg_length = boost::asio::read(sock, boost::asio::buffer(msg, msglen));

        Json::Reader reader;
        reader.parse(std::string(msg, msg_length), root);
        std::cout << "msg id is " << root["id"] << " msg is " << root["data"] << endl;
        std::getchar();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << endl;
    }
    return 0;
}

服务器

服务也通常和客户端做相同的设置,包含库。并在回调读函数中新加这样一段

			//jsoncpp序列化
			Json::Reader reader;
			Json::Value root;
			reader.parse(std::string(_recv_msg_node->_msg, _recv_msg_node->_total_len), root);
			std::cout << "recevie msg id  is " << root["id"].asInt() << " msg data is "
				<< root["data"].asString() << endl;
			root["data"] = "Server has received msg, msg data is " + root["data"].asString();
			std::string return_str = root.toStyledString();
			Send_protoc(return_str);

完整的回调读函数如下:

void CSession::handle_read(const boost::system::error_code& error, size_t bytes_transferred,
	std::shared_ptr<CSession> _self_shared) {
	if (!error) {

		// 打印缓存区的数据并将该线程暂停2s
		//PrintRecvData(_data, bytes_transferred);
		//std::chrono::milliseconds dura(2000);
		//std::this_thread::sleep_for(dura);

		// 每触发一次handale_read,它会返回实际读取的字节数bytes_transferred,copy_len表示已处理的长度,每处理一字节,copy_len便加一
		int copy_len = 0; // 已经处理的字符数
		while (bytes_transferred > 0) { // 只要读取到数据就对其处理
			if (!_b_head_parse) { // 判断消息头部是否已处理,_b_head_parse默认为false
				// 异步读取到的字节数 + 已接收到的头部长度 < 头部总长度
				if (bytes_transferred + _recv_head_node->_cur_len < HEAD_LENGTH) { // 收到的数据长度小于头部长度,说明头部还未全部读取
					// 如果未完全接收消息头,则将接收到的数据复制到头部缓冲区
					// _recv_head_node->_msg,更新当前头部的接收长度,并继续异步读取剩余数据。
					memcpy(_recv_head_node->_msg + _recv_head_node->_cur_len, _data + copy_len, bytes_transferred);
					_recv_head_node->_cur_len += bytes_transferred;
					// 缓冲区清零,无需更新copy_len追踪已处理的字符数,因为之前读取的数据已经全部写入头部节点,下一个
					// 读入的消息从头开始(copy_len=0)往头节点写
					::memset(_data, 0, MAX_LENGTH);
					// 继续读消息
					_socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::headle_read, this,
						std::placeholders::_1, std::placeholders::_2, _self_shared));
					return;
				}

				// 如果接收到的数据量足够处理消息头部,则计算头部剩余的未接收字节,
				// 并将其从 _data 缓冲区复制到头部消息缓冲区 _recv_head_node->_msg
				int head_remain = HEAD_LENGTH - _recv_head_node->_cur_len; // 头部剩余未复制的长度
				// 填充头部节点
				memcpy(_recv_head_node->_msg + _recv_head_node->_cur_len, _data + copy_len, head_remain);
				copy_len += head_remain; // 更新已处理的data长度
				bytes_transferred -= head_remain; // 更新剩余未处理的长度

				short data_len = 0; // 获取头部数据(消息长度)
				memcpy(&data_len, _recv_head_node->_msg, HEAD_LENGTH);
				//网络字节序转化为本地字节序
				data_len = boost::asio::detail::socket_ops::network_to_host_short(data_len);
				cout << "data_len is " << data_len << endl;
				
				if (data_len > MAX_LENGTH) { // 判断头部长度是否非法
					std::cout << "invalid data length is " << data_len << endl;
					_server->ClearSession(_uuid);
					return;
				}

				_recv_msg_node = std::make_shared<MsgNode>(data_len); // 已知数据长度data_len,构建消息内容载体
				//消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里
				if (bytes_transferred < data_len) {
					memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred);
					_recv_msg_node->_cur_len += bytes_transferred;
					// copy_len不用更新,缓冲区会清零,下一个读入data的数据从头开始写入,copy_len也会被初始化为0
					::memset(_data, 0, MAX_LENGTH);
					_socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH),
						std::bind(&CSession::headle_read, this, std::placeholders::_1, std::placeholders::_2, _self_shared));
					
					_b_head_parse = true; //头部处理完成
					return;
				}

				// 接收的长度多于消息内容长度
				memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, data_len);
				_recv_msg_node->_cur_len += data_len;
				copy_len += data_len;
				bytes_transferred -= data_len;
				_recv_msg_node->_msg[_recv_msg_node->_total_len] = '\0';
				// cout << "receive data is " << _recv_msg_node->_msg << endl;

				// protobuf序列化
				//MsgData msgdata;
				//std::string receive_data;
				//msgdata.ParseFromString(std::string(_recv_msg_node->_msg, _recv_msg_node->_total_len));
				//std::cout << "receive msg id is " << msgdata.id () << " msg data is  " << msgdata.data() << endl;
				//std::string return_str = "Server has received msg, msg data is " + msgdata.data();
				//MsgData msgreturn;
				//msgreturn.set_id(msgdata.id());
				//msgreturn.set_data(return_str);
				//msgreturn.SerializeToString(&return_str);
				//Send_protoc(return_str);

				// jsoncpp序列化
				Json::Reader reader;
				Json::Value root;
				reader.parse(std::string(_recv_msg_node->_msg, _recv_msg_node->_total_len), root);
				std::cout << "recevie msg id  is " << root["id"].asInt() << " msg data is "
					<< root["data"].asString() << endl;
				root["data"] = "Server has received msg, msg data is " + root["data"].asString();
				std::string return_str = root.toStyledString();
				Send_protoc(return_str);

				//Send(_recv_msg_node->_msg, _recv_msg_node->_total_len); // 回传
				// 清理已处理的头部消息并重置,准备解析下一条消息
				_b_head_parse = false;
				_recv_head_node->Clear();

				// 如果当前数据已经全部处理完,重置缓冲区 _data,并继续异步读取新的数据
				if (bytes_transferred <= 0) {
					::memset(_data, 0, MAX_LENGTH);
					_socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH),
						std::bind(&CSession::headle_read, this, std::placeholders::_1, std::placeholders::_2, _self_shared));
					return;
				}

				continue; // 异步读取的消息未处理完,继续填充头节点乃至新的消息节点
			}

			//已经处理完头部,处理上次未接受完的消息数据
			int remain_msg = _recv_msg_node->_total_len - _recv_msg_node->_cur_len;
			if (bytes_transferred < remain_msg) { //接收的数据仍不足剩余未处理的
				memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred);
				_recv_msg_node->_cur_len += bytes_transferred;
				::memset(_data, 0, MAX_LENGTH);
				_socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH),
					std::bind(&CSession::headle_read, this, std::placeholders::_1, std::placeholders::_2, _self_shared));
				return;
			}
			// 接收的数据多于剩余未处理的长度
			memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, remain_msg);
			_recv_msg_node->_cur_len += remain_msg;
			bytes_transferred -= remain_msg;
			copy_len += remain_msg;
			_recv_msg_node->_msg[_recv_msg_node->_total_len] = '\0';
			//cout << "receive data is " << _recv_msg_node->_msg << endl;

			// protobuf序列化
			//MsgData msgdata;
			//std::string receive_data;
			//msgdata.ParseFromString(std::string(_recv_msg_node->_msg, _recv_msg_node->_total_len));
			//std::cout << "receive msg id is " << msgdata.id() << " msg data is  " << msgdata.data() << endl;
			//std::string return_str = "Server has received msg, msg data is " + msgdata.data();
			//MsgData msgreturn;
			//msgreturn.set_id(msgdata.id());
			//msgreturn.set_data(return_str);
			//msgreturn.SerializeToString(&return_str);
			//Send_protoc(return_str);

			//jsoncpp序列化
			Json::Reader reader;
			Json::Value root;
			reader.parse(std::string(_recv_msg_node->_msg, _recv_msg_node->_total_len), root);
			std::cout << "recevie msg id  is " << root["id"].asInt() << " msg data is "
				<< root["data"].asString() << endl;
			root["data"] = "Server has received msg, msg data is " + root["data"].asString();
			std::string return_str = root.toStyledString();
			Send_protoc(return_str);

			//此处可以调用Send发送测试
			//Send(_recv_msg_node->_msg, _recv_msg_node->_total_len);
			//继续轮询剩余未处理数据
			_b_head_parse = false;
			_recv_head_node->Clear();
			if (bytes_transferred <= 0) {
				::memset(_data, 0, MAX_LENGTH);
				_socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH),
					std::bind(&CSession::headle_read, this, std::placeholders::_1, std::placeholders::_2, _self_shared));
				return;
			}
			continue;
		}
	}
	else {
		std::cout << "handle read failed, error is " << error.what() << endl;
		Close();
		_server->ClearSession(_uuid);
	}
}

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

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

相关文章

C# 委托(Delegate)二

一.委托的多播&#xff08;Multicasting of a Delegate&#xff09;&#xff1a; 委托对象&#xff0c;使用 "" 运算符进行合并&#xff0c;一个合并委托调用它所合并的两个委托。使用"-" 运算符从合并的委托中移除组件委托。 注&#xff1a;只有相同类型…

使用 Napkins.dev 将草图转换为应用程序

在现代前端开发中&#xff0c;快速将设计草图转换为实际的应用程序代码是一个巨大的优势。Napkins.dev 是一个利用人工智能将网站设计草图转换成实际应用程序的平台。本文将介绍如何使用 Napkins.dev 进行这一过程。 什么是 Napkins.dev&#xff1f; Napkins.dev 是一个开源平…

机器学习-TopicModel

概率图模型基础概率潜在语义分析&#xff08;PLSA&#xff09;LDA 概率图模型基础 猜球游戏 有两个信封&#xff0c;其中一个装有一个红球&#xff0c;一个黑球。另一个信封有两个黑球。 。 假设红球价值100元&#xff0c;黑球价值1元。 你随机从其中拿起一个信封&#xff0c;从…

Linux高阶IO之select多路转接

文章目录 select多路转接timeoutfd_set返回值执行过程总结 select多路转接 多路转接有三种方案,分别是select,poll和epoll,我们都会讲解和介绍 select的函数原型是这样的 他一共有五个参数,但是可以分为三组 nfds:需要监视的最大的文件描述符值1readfds:可读文件描述符集合 …

C#邮件发送:实现自动化邮件通知完整指南!

C#邮件发送性能怎么优化&#xff1f;使用C#发送邮件的设置步骤&#xff1f; 无论是用于客户服务、内部沟通还是项目管理&#xff0c;自动化邮件通知都能显著提高效率和响应速度。AokSend将详细介绍如何使用C#邮件发送功能来实现自动化邮件通知系统。 C#邮件发送&#xff1a;配…

二、词法分析,《编译原理》(本科教学版),第2版

文章目录 一、词法分析器1.1 词法分析器的作用1.2 词法分析器的设计方法1.3Antlr 词法分析器生成器1.3.1 环境准备1.3.2 词法分析器自动生成初体验&#xff08;需了解少许正则表达式概念&#xff09;1.3.2.1 创建工程1.3.2.2 空白符逻辑1.3.2.3 实现INT类型1.3.2.4 实现单行注释…

ClkLog常见问题-埋点集成篇Sec. 2

本篇将继续解答ClkLog使用过程中【埋点集成】阶段的常见问题。 1.【埋点集成】 问&#xff1a;receiver数据接收是不是一定要有ssl证书&#xff1f; 答&#xff1a;不是。 2.【埋点集成】 问&#xff1a;接收服务地址从哪里获取&#xff1f; 答&#xff1a;接收服务地址参考&am…

死磕P7: JVM垃圾回收那点事,轻松拿捏不是事儿(一)

这是「死磕P7」系列第 003 篇文章&#xff0c;欢迎大家来跟我一起 死磕 100 天&#xff0c;争取在 2025 年来临之际&#xff0c;给自己一个交代。 上两篇介绍了 JVM 内存区域划分&#xff0c;简单记忆一下就可以了&#xff0c;后面再不断深入吧。 死磕P7: JVM内存划分必知必会…

Spring邮件发送:配置与发送邮件详细步骤?

Spring邮件发送教程指南&#xff1f;怎么用Spring邮件发送服务&#xff1f; Spring框架提供了强大的邮件发送支持&#xff0c;使得开发者能够轻松地在应用程序中集成邮件发送功能。AokSend将详细介绍如何在Spring应用中配置和发送邮件&#xff0c;帮助开发者快速掌握这一关键技…

浙大数据结构:06-图2 Saving James Bond - Easy Version

这道题是稍复杂版的dfs或bfs&#xff0c;此处我采用bfs实现 机翻&#xff1a; 1、条件准备 n为鳄鱼数量&#xff0c;jump为跳一次最大距离。 eyu数组对存每条鳄鱼的坐标位置&#xff0c;visit数组判断该鳄鱼是否走过&#xff0c;isalive判断到达该鳄鱼时能否逃离。 #includ…

探索图像生成大模型Imagen:原理、比较与应用

目录 目录 1. 引言 2. 图像生成模型的发展背景 2.1 生成对抗网络&#xff08;GAN&#xff09; 2.2 变分自编码器&#xff08;VAE&#xff09; 2.3 自回归模型 2.4 扩散模型 3. Imagen简介 3.1 模型架构 3.2 关键技术 3.3 训练数据与方法 4. Imagen与其他模型的比较 …

ArcGIS Pro高级地图可视化—双变量符号地图

ArcGIS Pro高级地图可视化 ——双变量符号地图 1 背景 “我不是双变量&#xff0c;但我很好奇。”出自2013 年南卡罗来纳州格林维尔举行的 NACIS 会议上&#xff0c;双变量地图随着这句俏皮的话便跳跃在人们的视角下&#xff0c;在讨论二元映射之后&#xff0c;它不仅恰逢其…

AOT源码解析4.5-AOT整体结构

论文阅读 papergithub论文阅读笔记AOT源码解析1-数据集处理AOT源码解析2-encoderdecoderAOT源码解析3-模型训练AOT源码解析4.1-model主体AOT源码解析4.2-model主体AOT源码解析4.3-model主体AOT源码解析4.4-model主体AOT源码解析4.5-model主体 4.1~4.4小节详细讲解了ref_imgs相…

15年408-数据结构

第一题 解析&#xff1a; 栈第一次应该存main的信息。 然后进入到main里面&#xff0c;要输出S(1)&#xff0c;将S(1)存入栈内&#xff0c; 进入到S(1)中&#xff0c;1>0,所以还要调用S(0) S(0)进入栈中&#xff0c;此时栈内从下至上依次是main(),S(1),S(0) 答案选A 第二题&…

Java基于相似算法实现以图搜图

一、简述 本文主要讲如何利用图片相似性算法&#xff0c;基于LIRE来实现图片搜索。 二、依赖 <dependencies><!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-core --><dependency><groupId>org.apache.lucene</groupId><…

Apache Solr:深入探索与常见误区解析

Apache Solr&#xff1a;深入探索与常见误区解析 Apache Solr 是一个强大的搜索引擎&#xff0c;基于 Lucene 构建&#xff0c;广泛应用于电商平台、日志分析、内容管理系统等领域。Solr 的功能强大&#xff0c;然而它的配置和使用过程却不乏一些容易误解和出错的地方。本文将…

Spring validation校验框架

第1步&#xff1a;导入依赖 <!-- 校验框架--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency> 第2步&#xff1a;为需要校验的参数&…

Leetcode 739.42. 每日温度 接雨水 单调栈 C++实现

问题&#xff1a;Leetcode 739. 每日温度 算法1&#xff1a;从右到左 栈中记录下一个更大元素的「候选项」。 代码&#xff1a; class Solution { public:vector<int> dailyTemperatures(vector<int>& temperatures) {int n temperatures.size();vector<…

【echarts】报错series.render is required.

总结&#xff1a;就是echarts无法保存renderItem函数到json里&#xff0c;因为renderItem是个封装方法&#xff0c;因此需要初始化加载时重新插入renderItem即可 1.描述&#xff1a;控制台报错series.render is required. 原数据json如下&#xff1a; {type: "bar"…

【数学分析笔记】第3章第4节闭区间上的连续函数(2)

3. 函数极限与连续函数 3.4 闭区间上的连续函数 3.4.4 中间值定理 【定理3.4.4】若 f ( x ) f(x) f(x)在 [ a , b ] [a,b] [a,b]上连续&#xff0c;则它一定能取到最大值 M M M与最小值 m m m之间的任何一个值。 M max ⁡ f ( x ) , x ∈ [ a , b ] , m min ⁡ f ( x ) , …