目录
一、json库安装
1)安装scons
2) 安装jsoncpp
二、yaml-cpp库安装
三、json.hpp的使用
四、客户端与服务端通过socket通信
1) 代码展示
2) 编译
3) 运行
总结:
使用平台说明:ubuntu18+vscode
实现功能说明:客户端读取本地提前写好的config.yaml文件,通过socket通信传输yaml文件的数据到服务端,服务端保存接收的数据到text.json文件。
涉及知识点:TCP的socket本地通信、JSON库的使用、yaml文件的读取、nlohmann/json中的json.hpp使用(注:此json.hpp非前面的JSON库)、对文件的读取以及写入操作。
因为要保存为json文件,所以需要提前配置c++的json运行环境
去网站下载源码编译安装:Download scons-2.1.0.tar.gz (SCons)
下载后,将压缩包放到/home下解压:
tar zxzvf scons-2.1.0.tar.gz
进入到scons-2.1.0
sudo python setup.py install
设置环境变量:
sudo gedit ~/.bashrc
在打开的系统环境文件尾部加上:
#json
export MYSCONS=/home/ssz/scons-2.1.0 //根据自己scons安装路径进行修改
export SCONS_LIB_DIR=$MYSCONS/engine
然后source一下
source ~/.bashrc
官网下载jsoncpp源码包:json-cpp - Browse Files at SourceForge.net
依然拷到home下解压:
tar -zxvf jsoncpp-src-0.5.0.tar.gz
然后:
cd jsoncpp-src-0.5.0
scons platform=linux-gcc
sudo mv libs/linux-gcc-7/libjson_linux-gcc-7_libmt.so /usr/lib
//此处gcc-7根据自己的gcc版本进行更改,也可以进入自己的libs文件夹查看是什么文件
sudo mv include/json/ /usr/include
使用时需要在头文件包含jsoncpp/json/json.h
#include "jsoncpp/json/json.h"
我的电脑不知道啥时候就安装好了,直接就用了,我安装的路径如下图
如果没安装的可以从github下载:GitHub - jbeder/yaml-cpp: A YAML parser and emitter in C++mirrors / jbeder / yaml-cpp · GitCodeGitHub - jbeder/yaml-cpp: A YAML parser and emitter in C++mirrors / jbeder / yaml-cpp · GitCode
移动include/yaml-cpp文件到本地计算机/usr/include里面就可以使用
使用时需要在头文件包含yaml-cpp/yaml.h
#include
我是从github下载的,仓库地址为:nlohmann/json: JSON for Modern C++ (github.com)
nlohomann/json
的所有代码都保存在单个头文件json.hpp
中,所以要使用nlohomann/json
,需要在实现文件中包含json.hpp
#include "json.hpp"
整个文件夹所有起始文件如下图所示:
客户端client.cpp代码如下:
#include
#include
#include
#include
#include
#include "json.hpp"
#include
#include
#include
#include
#include "ProtocolParser.h"
#include
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::string;
using nlohmann::json;
using std::vector;
int orign_x;
int orign_y;
float con;
int con1;
float ex;
int ex1;
int min_obsR;
int max_obsR;
vector vec;
void test()
{
int clientfd = ::socket(AF_INET,SOCK_STREAM,0);
if(clientfd < 0)
{
perror("socket failed!");
return;
}
struct sockaddr_in serverAddr;
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8800);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t length = sizeof(serverAddr);
if(::connect(clientfd,(struct sockaddr*)&serverAddr,length) < 0)
{
perror("connect");
close(clientfd);
return;
}
printf("conn has connected!...\n");
while(1)
{
char sendData[2048] = {0};
YAML::Node config = YAML::LoadFile("/home/ssz/401_serch-tcp_4/client_server_test/config/config.yaml");
orign_x = config["orign_x"].as();
orign_y = config["orign_y"].as();
con = config["con"].as();
ex = config["ex"].as();
min_obsR = config["min_obsR"].as();
max_obsR = config["max_obsR"].as();
con1 = con * 10000;
ex1 = ex * 10000;
vec.push_back(orign_x);
vec.push_back(orign_y);
vec.push_back(con1);
vec.push_back(ex1);
vec.push_back(min_obsR);
vec.push_back(max_obsR);
ProtocolParser parser;
json j = parser.vector2json(vec);
// cout << "j = " << j << endl;
parser.Json2char(j,sendData);
// cout << "strlen(sendData) = " << strlen(sendData) << endl;
// //1.客户端先发数据
cout << "sendData = " << sendData << endl;
send(clientfd,sendData,strlen(sendData),0);
// char buff[128] = {0};
// recv(clientfd,buff,sizeof(buff),0);
// printf("recv msg from server:%s\n",buff);
}
close(clientfd);
}
int main(void)
{
test();
return 0;
}
服务端server.cpp代码如下:
#include
#include
#include
#include
#include
#include "json.hpp"
#include "jsoncpp/json/json.h"
#include
#include
#include
#include
#include
#include "ProtocolParser.h"
#include
#include
#include
#include
using namespace std;
using nlohmann::json;
void test()
{
//1.创建监听服务器的套接字
int listenfd = ::socket(AF_INET,SOCK_STREAM,0);
if(listenfd < 0)
{
perror("socket failed!");
return;
}
//网络地址序需要采用网络字节序存储(大端模式)
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(8800);
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t length = sizeof(serveraddr);
#if 1
//设置网络地址可以重用
int on =1;
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int)) < 0)
{
perror("setsockopt");
close(listenfd);
return;
}
//当端口设置为可以重用时,就实现了系统级别的负载均衡
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEPORT,&on,sizeof(int)) < 0)
{
perror("setsockopt");
close(listenfd);
return;
}
#endif
//2.绑定服务器的网络地址
if(::bind(listenfd,(struct sockaddr*)&serveraddr,length) < 0)
{
perror("bind");
//文件描述符是比较稀缺的,所以不用的时候要回收
close(listenfd);
return;
}
//3.让服务器开始监听
if(::listen(listenfd,10) < 0)// 监听它,并设置最大连接数为10
{
perror("listen");
close(listenfd);
return;
}
printf("server is listening...\n");
//4.获取对端的文件描述符,并与对端进行通信
// int peerfd = ::accept(listenfd, nullptr, nullptr);
//5.需求:希望服务器可以与客户端通过终端进行聊天
while(1)
{
int peerfd = ::accept(listenfd, nullptr, nullptr);
char recvdata[2048];
int ret = recv(peerfd,recvdata,2048,0);
// cout << "ret = " << ret <>pls input some message:";
// string line;
// getline(cin,line);
// send(peerfd,line.c_str(),line.size(),0);
}
// close(peerfd);//关闭连接
close(listenfd);
}
int main(void)
{
test();
return 0;
}
ProtocolParser.h文件封装了一个用于json与内置数据类型与STL容器转换的类,代码如下:
#include "json.hpp"
#include
#include
using std::string;
using std::vector;
using nlohmann::json;
class ProtocolParser
{
public:
void Json2char(json &j,char* sendData);//json转char数组
json doParse(char* recvData);//char数组转json
json vector2json(vector &s);//vector转json
void json2vector(json &j,vector &v);//json转vector
};
void ProtocolParser::Json2char(json &j, char* sendData)
{
string s = j.dump();
strcpy(sendData,s.c_str());
}
json ProtocolParser::doParse(char* recvData)
{
return json::parse(recvData);
}
json ProtocolParser::vector2json(vector &s)
{
return json(s);
}
void ProtocolParser::json2vector(json &j,vector &v)
{
v.clear();
v = j.get>();
}
json.hpp代码从前文提到的仓库地址下载,代码太长,这里就不放了。
config文件夹里面是个config.yaml文件,代码如下:
orign_x: 407 #地图原点
orign_y: 472
con: 0.95 #系数
ex: 0.543 #指数
min_obsR: 80 #结束条件
max_obsR: 130
服务端编译:
g++ server.cpp -o server -ljsoncpp
客户端编译:
g++ client.cpp -lyaml-cpp -o client
先启动服务端,然后启动客户端,运行结果如下图所示:
此时客户端读取yaml文件里面的数据,通过socket通信把数据传输给服务端, 服务端创建一个test.json文件并保存接收到的数据,test.json文件内容如下:
{
"con" : 0.94999998807907104,
"ex" : 0.5429999828338623,
"max_obsR" : 130,
"min_obsR" : 80,
"orign_x" : 407,
"orign_y" : 472
}
修改yaml里的文件数据orign_x为450,此时什么都不需要编译,直接再次执行客户端,可以看到test.json里的orign_x数据也变为了450
test.json文件内容如下:
{
"con" : 0.94999998807907104,
"ex" : 0.5429999828338623,
"max_obsR" : 130,
"min_obsR" : 80,
"orign_x" : 450,
"orign_y" : 472
}
至此,所有任务完成!!
这个项目是在本地完成客户端与服务端的数据传输,做这个小项目的目的是为了引出下一个项目在同一局域网条件下,电脑(ubuntu18)和工控机(ubuntu16)进行TCP远程无线通信。此时电脑为客户端,工控机为服务端,电脑里面的yaml文件修改数据,就可以远程发送给工控机里的test.json文件,从而工控机里面的其余代码就可以调用这个test.json文件。下一个项目参看下一篇博客。
参考资源:
UBUNTU16.04 搭建c++ json和curl运行环境_ubuntu curl c++_berry丶的博客-CSDN博客
C++使用json在Linux中实现TCP通信_linux c++ json_qq_21197941的博客-CSDN博客
c++读取yaml文件_c++ yaml_Hello_RoyChen的博客-CSDN博客