Linux C++使用TCP通信实现本地客户端与服务端传输文件数据(亲测可用)

目录

一、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运行环境

一、json库安装

1)安装scons

去网站下载源码编译安装: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

2)  安装jsoncpp

官网下载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"

二、yaml-cpp库安装

我的电脑不知道啥时候就安装好了,直接就用了,我安装的路径如下图

如果没安装的可以从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

三、json.hpp的使用

我是从github下载的,仓库地址为:nlohmann/json: JSON for Modern C++ (github.com)

nlohomann/json的所有代码都保存在单个头文件json.hpp中,所以要使用nlohomann/json,需要在实现文件中包含json.hpp

#include "json.hpp"

四、客户端与服务端通过socket通信

整个文件夹所有起始文件如下图所示:

1)  代码展示

客户端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

2)  编译

服务端编译:

g++ server.cpp -o server -ljsoncpp

客户端编译:

g++ client.cpp -lyaml-cpp -o client

3)  运行

先启动服务端,然后启动客户端,运行结果如下图所示:

此时客户端读取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博客

你可能感兴趣的:(Linux网络编程,C++,linux,tcp/ip,运维,c++)