C++使用json在Linux中实现TCP通信

最近在学习使用json格式在Linux中进行tcp通信,以下为学习笔记

我使用的第三方库为nlohomann/json, 仓库地址为

nlohmann/json: JSON for Modern C++ (github.com)

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

#include "json.hpp"
json的发送与接收

要发送json格式的数据时需要先将其转换为二进制数据,我使用以下代码实现转换

    json j;
    char sendData[2048];
    ...
    string s = j.dump();//将json转换为string类型
    strcpy(sendData, s.c_str());//将string类型转为char*后进行拷贝

在接收到json的二进制数据后,再将其转换为json格式

recv(peerfd, recvdata, 2048, 0);
json j = json::doParse(recvData);
代码实例

我封装了一个用于json与内置数据类型与STL容器转换的类

#include "json.hpp"
#include <vector>
#include <string>
class ProtocolParser
{
    public:
        void Json2char(json & j, char*  sendData);//json转char数组
        json doParse(char*  recvData);//char数组转json
        json vector2json(vector<string> & s);//vector转json
        void json2vector(json & j, vector<string> & v);//json转vector
};

using std::string;

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<string> & s)
{
    return json(s);
}

void ProtocolParser::json2vector(json & j, vector<string> & v)
{
    v.clear();
    v = j.get<vector<string>>();
}

以下为在客户端与服务端中使用ProtocolParse类的示例

服务端

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "json.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ProtocolParser.h"

#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using nlohmann::json;

void test()
{
  //1. 创建监听服务器的套接字
  int listenfd = ::socket(AF_INET, SOCK_STREAM, 0);  

  if(listenfd < 0)
    {
    perror("socket");
    return;
  }

  //网络地址需要采用网络字节序存储(大端模式)
  struct sockaddr_in serveraddr;
  memset(&serveraddr, 0, sizeof(serveraddr));
  serveraddr.sin_family = AF_INET;
  serveraddr.sin_port = htons(8888);
  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)
    {
    perror("listen");
    close(listenfd);
    return;
  } 
  printf("server is listening...\n");

  //4. 获取对端的文件描述符,并与对端进行通信
  int peerfd = ::accept(listenfd, nullptr, nullptr);

  //5. 需求:希望服务器可以与客户端通过终端进行聊天
  while(1) 
    {
    char recvdata[2048];
    int ret = recv(peerfd, recvdata, 2048, 0);
    printf("recv msg from client: %s\n", recvdata);
    recvdata[ret] = '\0';
    ProtocolParser parse;
    json j = parse.doParse(recvdata);
    cout << j << endl;

    //从终端获取一行数据
    cout << ">> 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;
}

客户端

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <vector>
#include "json.hpp"
#include "ProtocolParser.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using nlohmann::json;
using std::vector;

void test()
{
  int clientfd = ::socket(AF_INET, SOCK_STREAM, 0);
  if (clientfd < 0)
  {
    perror("socket");
    return;
  }

  struct sockaddr_in serverAddr;
  memset(&serverAddr, 0, sizeof(serverAddr));
  serverAddr.sin_family = AF_INET;
  serverAddr.sin_port = htons(8888);
  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)
  {
    // string line;
    // cout << ">> pls input some message:";
    // getline(cin, line);
    // line.append("\n");
    char sendData[2048] = {0};
    
    vector<string> vec={"hello", "world"};
    ProtocolParser parser;
    json j = parser.vector2json(vec);
    parser.Json2char(j, sendData);
    // 1. 客户端先发数据
    send(clientfd, sendData, strlen(sendData), 0);
    /* close(clientfd); */

    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;
}

运行结果

服务端进程

在这里插入图片描述

客户端进程

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值