C++のJSONラブラリ  (JSON for Modern C++)と、JSONのUDPでの運び方

2021年3月10日

C++でJSONを扱わなければならなくなりました。

blog.livedoor.jp/tek_nishi/archives/10216517.html

に、

JSON for Modern C++ を紹介するよ!
ブログに書いた書いたと思い込んでてまったく触れてなかった...
このJSON読み書きライブラリは他のライブラリに依存しておらず、json.hppをインクルードするだけというお手軽さながら、JSONオブジェクトをめっちゃ気楽に扱えるようにしてくれます。

なん・・だと・・。インクルードするだけ・・ 採用決定!!

となりました。

で、早速、

$ git clone https://github.com/nlohmann/json.git

をして、

// g++ test_json.cpp -o test_json -I json/include/
// g++ test_json.cpp -o test_json -I json/include/

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

int main(int argc, char **argv){

  using json = nlohmann::json;

  json j;

  j["pi"] = 3.141;
  j["happy"] = true;
  j["name"] = "Niels";
  j["nothing"] = nullptr;
  j["answer"]["everything"] = 42;

  j["list"] = { 1, 0, 2 };         // [1,0,2]
  j["object"] = { {"currency", "USD"}, {"value", 42.99} };  // {"currentcy": "USD", "value": 42.99} 

  std::cout << j << std::endl;  // cout

  return 0;
}

をした後、

>g++ test_json.cpp -o test_json -I c:/Users/Ebata/json/include  (git cloneが展開された場所にjson/includeというディレクトリができているので、そこをダイレクトに指示する)

を行い、./test_json を実施したら、

さくっと動きました。

では、次にこのJSONをUDP通信で飛ばしてみます。

ゲストOSがLinuxのDockerコンテナの中から、ホストOSにデータを放り出す為に使ったUDPプログラム

この↑のプログラムを前提とします

■C++送信プログラム (キモは、std::string jj = j.dump(); です)

// g++ test_send_json.cpp -o test_send_json -I C:/Users/Ebata/json/include/ -lwsock32 -lws2_32 (Windows版)

#include <stdio.h>
#include <string.h>
#include <nlohmann/json.hpp>  
#include <iostream>
#include "simple_udp_win.h" // https://wp.kobore.net/江端さんの技術メモ/post-1959/
//#include "simple_udp.h"

//simple_udp udp0("0.0.0.0",12345); // これはWindowでは動かない
//simple_udp udp0("192.168.0.8",12345); // これは使わない方がいい
simple_udp udp0("127.0.0.1",12345); // ホストOSのUDPの受信側

int main(int argc, char **argv){

  using json = nlohmann::json;

  json j;

  j["pi"] = 3.141;
  j["happy"] = true;
  j["name"] = "Niels";
  j["nothing"] = nullptr;
  j["answer"]["everything"] = 42;

  j["list"] = { 1, 0, 2 };         // [1,0,2]
  j["object"] = { {"currency", "USD"}, {"value", 42.99} };  // {"currentcy": "USD", "value": 42.99} 

  std::cout << j << std::endl;  // cout

  std::string jj = j.dump(); // これが重要(これを見つけるのに時間がかかった)

  udp0.udp_send(jj);

  return 0;
}

■C++受信プログラム (キモは、json j = json::parse(rdata); です)

// g++ test_recv_json.cpp -o test_recv_json -I C:/Users/Ebata/json/include/ -lwsock32 -lws2_32

#include <stdio.h>
#include <string.h>
#include <nlohmann/json.hpp>  
#include <iostream>
#include "simple_udp_win.h" // https://wp.kobore.net/江端さんの技術メモ/post-1959/
//#include "simple_udp.h"

//simple_udp udp0("0.0.0.0",12345);  // これはWindowsでは動かない
//simple_udp udp0("192.168.0.8",12345); // これは使わない方がいい
simple_udp udp0("127.0.0.1",12345); // これを使うのが無難


int main(int argc, char **argv){
  
  using json = nlohmann::json;

  json j;
  
  udp0.udp_bind();
  while (1){
    std::string rdata=udp0.udp_recv();

    j = json::parse(rdata); // これが重要(これを見つけるのに時間がかかった)
    // j = json::parse(rdata.c_str()); これでも動くようだが

    std::cout << j << std::endl;  // cout
   
  }
  return 0;
}

さて、そろそろ詰めに入ろうか。

■GOのJSONのUDP受信プログラム(上記の「C++送信プログラム」と対向で動くもの)

// test_recv_json.go
// go run test_recv_json.go
// golangによるjsonのudp受信"だけ"するプログラム

package main

import (
	"encoding/json"
	"fmt"
	"net"
)

type Message struct {  // とりあえず3つ
	Pi    float64 `json:"pi"`
	Happy bool    `json:"happy"`
	Name  string  `json:"name"`
}

func main() {
	addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:12345")
	sock, _ := net.ListenUDP("udp", addr)

	var j Message

	i := 0
	for {
		i++
		buf := make([]byte, 1024)
		rlen, _, err := sock.ReadFromUDP(buf)
		if err != nil {
			fmt.Println(err)
		}
		str := string(buf[0:rlen])
		fmt.Println(str)

		// 受信したメッセージをJSON形式に格納する
		json.Unmarshal(buf[0:rlen], &j)

		// JSONに格納したデータを見る
		fmt.Println(j)

		fmt.Println(j.Pi)
		fmt.Println(j.Happy)
		fmt.Println(j.Name)

	}
}

■GOのJSONのUDP送信プログラム(上記の「C++受信プログラム」と対向で動くもの)

// test_send_json.go
// go run test_send_json.go
// golangによるjsonのudp送信"だけ"するプログラム

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
)

type Message struct { // とりあえず3つ
	Pi    float64 `json:"pi"`
	Happy bool    `json:"happy"`
	Name  string  `json:"name"`
}

func main() {

	addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12345")
	if err != nil {
		log.Fatal(err)
	}

	conn, _ := net.DialUDP("udp", nil, addr)

	if err != nil {
		log.Fatal(err)
	}

	var j Message
	j.Pi = 3.1415
	j.Happy = false
	j.Name = "Ebata"

	s, _ := json.Marshal(j)

	n, err := conn.Write(s) // WriteToUDPを使ってはならない
	fmt.Printf("dial send: bytes=%d to=%s\n", n, addr.String())

}

 

以上

2021年3月10日2021/03,江端さんの技術メモ

Posted by ebata