상세 컨텐츠

본문 제목

[Node.js] Node.js에서 C++의 struct 비슷기능[cpp-struct-js] 이용하기, C/C++ TCP/IP소켓 통신시 struct를 버퍼를 보내거나 받을때 변환 관련

WEB/JavaScript

by AlrepondTech 2018. 10. 11. 17:57

본문

반응형

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

출처ㅣ https://www.npmjs.com/package/cpp-struct-js

 

 

cpp-struct-js

The intention of this module is to interace node.js with an Arduino software I am writing. For this I need to read binary structures that are generated by C++ code running on an Arduino system.

Supported types:

  • uint8,16,32
  • int8,16,32
  • float
  • strings

Only one dimensional arrays are supported. It might be possible to extend the code to support this, but I don't see a need for this right now.

Pointers are not supported, but that wouldn't make sense anyway since serialization can't deal with native memory adresses.

Example

 
struct = require("cpp-struct")
 
var player = new struct("Player", [
    "name", struct.char(12),
    "id", struct.uint32_t()
]);
 
var record = new struct("Record", [
    "playerIndex", struct.uint8_t(),
    "typeId", struct.uint8_t(),
    "record", struct.uint16_t(31)
]);
 
var EEPROMData = new struct("EEPROMData", [
    "gameName", struct.char(14),
    "version", struct.uint16_t(),
    "players", struct.type(player,32),
    "records", struct.type(record,32)
]);
 
var buffer = new Buffer(EEPROMData.size());
EEPROMData.encode(buffer,0, {
    gameName: "SuperTesting!!!",
    version: 1,
    players:[
        {name:"Hello",id:1},
        {id:2},
        {name:"HUHU"}
    ]
},{endian:"LE"})
 

Reference

Class: struct (var struct = require('cpp-struct'))

Constructor signature: name, schema, [count, [bytes]]

  • name: Internal name that's used when exporting and referencing other structs.
  • schema: Interleaved array where even elements are variable name identifiers and odd elements are struct instances
  • count (optional): If the type is supposed to be an array, this is the number of elements in the array
  • bytes (optional): Size of a single element in bytes (total size = count * bytes)

Note: count and bytes arguments are used usually only internally - you don't have to worry about these if you don't intend to add new native types next to the currently implemented ones such as int/uint/float/double - say you'd want a int24, you'll need to care about these arguments.

Instance member: struct.setEncoder

Function signature: func

  • func (buffer,pos,data,opt): Function that's called instead of the default encoder -- buffer: Buffer object to write to -- pos: position to write to -- data: the data value to be written (can be undefined) -- opt: optional options object that specifies encoding details (such as endianess)

Instance member: struct.setDecoder

 

추가 설명 .. decode에 대해서

 

//여기 모듈 "cpp-struct-js"에서 "decode(...)"함수의 설명이 없어서 따로 추가 설명한다.

 

//ES6 이상 모듈 방식으로 import, export 사용 아래와같이 node.js[npm] 에서

//(cpp-struct-js)을 인스톨해주고 아래와 같이 코드에 import 로 추가해준다.

 

import STRUCTCPP from "cpp-struct-js"; //es6 이상 버전에서 모듈화 import

 

var structT= new STRUCTCPP("structT", [
    "test1", STRUCTCPP.char(12),
    "test2", STRUCTCPP.uint32_t(),
]);
 
var structT2= new STRUCTCPP ("structT2", [
    "test3", STRUCTCPP .char(12),
    "test4", STRUCTCPP .uint32_t(),
    "test5", STRUCTCPP .uint32_t(),
    "info", STRUCTCPP.type(structT, 3), //이거는 info[3] 배열과 같은 뜻이라 보면 된다.
]);
 
 
//----------------------------------------------------------------------
// encode 할때.
var buffer = new Buffer(structT2.size());
structT2.encode(buffer, 0, {
    test3: "Testing!!!",
    test4: 25,
    info:[
        {test1:"Hello"},               //배열 info[0]
        {test2: 6},                     //배열 info[1]
        {test1:"good", test2: 2},    //배열 info[2]
    ]
},{endian: "LE"});
 
 
 
//----------------------------------------------------------------------
// decode 할때.
 

var decodeDat = structT2.decode(buffer , 0, {endian:"LE"}); //위의 encode 할때 아까 변수값 "buffer"

 

var test3Dat =  decodeDat.test3;    //값: Testing!!!

var test4Dat =  decodeDat.test4;    //값: 25

var infoDat  =  decodeDat.info;     //값: structT2->info

 

var  infoDatTest0_1 = infoDat[0].test1; //값: "Hello"

var  infoDatTest0_2 = infoDat[0].test2; //값: null

 

var  infoDatTest1_1 = infoDat[1].test1; //값: null

var  infoDatTest1_2 = infoDat[1].test2; //값: 6

 

var  infoDatTest2_1 = infoDat[2].test1; //값: "good"

var  infoDatTest2_2 = infoDat[2].test2; //값: 2

 

 

 

 

 

//ES6 이상 모듈 import, export 사용 아래와같이 node.js[npm] 

//에서(cpp-struct-js, iconv, node-buffertrim)을 인스톨해주고

//아래와 같이 코드에 import 로 추가해준다.

 

import STRUCTCPP from "cpp-struct-js";     //설명: struct-cpp 모듈

import ICONV from "iconv";                      //버퍼를 "utf8" "euc-kr"등 인코딩해주는 모듈

import BUFFTRIM from "node-buffertrim";   //버퍼의 양옆의 "0" 값을 제거, 버퍼트림기능 모듈

 

 

 

Node.JS와 C/C++ TCP/IP 소켓통신할때 참고

(개인적인 방법으로 구성해 보았습니다, 시스템 구성에 따라 다르니 참고용 입니다~!)

 

//---------------------------------------------------------------------

//---------------------------------------------------------------------

//C/C++

 

#define T2O =16;

 

typedef struct _stTest1

{

   char            test1Str[T2O];

   unsigned int test2uint;

 

   void  init(){

       memset(test1Str, 0, T2O);

       test2uint= 0;

   }

   _stTest1(){ init(); }

}stTest1;

 

 

typedef struct _stTest2

{

   unsigned int  testAuint;

   char             testBstr[T2O];

   unsigned int  testCuint;

   stTest1         testD[3];

 

   void  init(){

       testAuint = 0;

       memset(testBstr, 0, T2O);

       testCuint = 0;

       testD[0].init();

       testD[1].init();

       testD[2].init();

   }

   _stTest2(){ init(); }

}stTest2;

 
 
 
 

//------------------------------------------------------------------------

//------------------------------------------------------------------------

//Node.js - struct-cpp

 

//JS의 Struct의 변수들의 값은 "uint8" 으로 받자.

 

export const T2O = 16; //대강 String 받을 크기

 

var stTest1 = new STRUCTCPP("stTest1", [

                "test1Str",  STRUCTCPP.uint8(T2O), //String

                "test2uint", STRUCTCPP.uint8(2),    //uint16

            ]); 

 

var stTest2 = new STRUCTCPP("stTest2", [

                "testAuint",  STRUCTCPP.uint8(2),     //uint16

                "testBstr",    STRUCTCPP.uint8(T2O), //String

                "testCuint",  STRUCTCPP.uint8(2),     //uint16

                "testD", STRUCTCPP.type(stTest1 , 3) //stTest1[3]

            ]); 

 

 

//-----------------------------------------------------------------

//encode 값을 넣을때 

 

let dat16_t1 = new Uint16Array([52]);

let dat8_t1  = new Uint8Array(dat16_t1.buffer);

 

let datStr_t2     = "test가나";

let datStrbuf_t2 = Buffer(datStr_t2);

 

let dat16_t3 = new Uint16Array([23]);

let dat8_t3  = new Uint8Array(dat16_t2.buffer);

 

let datStr_p1     = "test다라";

let datStrbuf_p1 = Buffer(datStr_p2);

 

let dat16_p2= new Uint16Array([123]);

let dat8_p2= new Uint8Array(dat16_p2.buffer);

 

var bufTest = new Buffer(stTest2.size());

stTest2.encode(bufTest, 0, {

    testAuint: dat8_t1,      //값:52

    testBstr: datStrbuf_t2,  //값:"test가나"

    testCuint: dat8_t3,      //값:23

    testD:[ 

        {test1Str: datStrbuf_p1},                            //배열 testD[0]->test1Str: "test다라"

        {test2uint: dat8_p2},                                 //배열 testD[1]->test2uint: 123

        {test1Str: datStrbuf_t2, test2uint: dat8_t1},     //배열 testD[2]->test1Str: "test가나", test2uint: 52

    ]

},{endian: "LE"});

 

 

//---------------------------------------------------------------

//C/C++ 소켓 TCP/IP 연결 패킷 보낼때.

 

import NET from "net"; //Node.js -> net

 

 let servAddr = '111.222.333.444'; //주소

 let servPort  =  2145; //포트

 

 let clientSocket = NET.Socket();

 clientSocket.connect(servPort, servAddr, function() {

     console.log('Client connected to: ' + servAddr + ':' + servPort);

     clientSocket.write(bufTest); //위의 bufTest 버퍼를 보낸다.

 });

 

 

//-----------------------------------------------------------------

//C/C++ 소켓 TCP/IP 연결 패킷 받을때

 

var bufTestRevc = null;

clientSocket.on('data', function(data) {

            console.log('Client data');

            console.log('Client received: ' + data+'##bytelen:');

 

             bufTestRevc = data;

        });

 

//-----------------------------------------------------------------

//decode 값을 가져올때

 

let decData = stTest2.decode(bufTestRevc, 0, {endian:"LE"});  //"bufTestRevc" 위의 가져온 패킷을 decode 한다.

 

let stTest2_testAuint_buf = new Buffer(decData.testAuint);

let stTest2_testAuint = stTest2_testAuint_buf.readUIntLE(0, stTest2_testAuint_buf.length); //값: 52

 
let stTest2_testBstr = new Buffer(decData.testBstr).toString(); //값:"test가나     "
 

let stTest2_testBstr_buf = new Buffer(decData.testBstr);

let stTest2_testBstr = stTest2_testBstr_buf.readUIntLE(0, stTest2_testBstr_buf.length); //값: 23

 

let testD_0 = decData.testD[0];

let testD_1 = decData.testD[1];

let testD_2 = decData.testD[2];

 

//testD_2 만 decode 해보겠다.

 

let testD_2_test1Str = new Buffer(testD_2.test1Str).toString();  //값:"test가나     "

 

let testD_2_test2uint_buf = new Buffer(testD_2.test2uint);

let testD_2_test2uint = testD_2_test2uint_buf.readUIntLE(0, testD_2_test2uint_buf.length); //값: 52

 
 

//이처럼 형변환 문제로 변환할게 많아서 아래 설명 ES6이상 코드스타일로 "class CProtUtil" 에서 

//클래스 함수형으로 묶어서 형변환에 맞게 만들어 두었다. 형변환에 필요함수부분은 아래 

//설명, 코드를 보면 되겠다.

 

 

 

 

=================================

=================================

=================================

 

 

반응형

 

728x90

 

 

 

Util -> class CProtUtil

 

//ES6이상용으로 Class를 이용하여 cpp-struct-js 에 필요한 형 변환 유틸구현

//(만약 C++과 소켓통신시 struct를 버퍼형으로 보낼때 형변환시 필요)

 

//Node.js 보안문제로"new Buffer(.)"는 ->"Buffer.from(.), Buffer.alloc(.)"식으로 교체된다.

 

//ES6 이상 모듈 import, export 사용 아래와같이 node.js[npm] 에서

//(cpp-struct-js, iconv, node-buffertrim)을 인스톨해주고

//아래와 같이 코드에 import 로 추가해준다.

 

 

import STRUCTCPP from "cpp-struct-js";

import ICONV from "iconv";

import BUFFTRIM from "node-buffertrim";

 

 

//-------------------------

//-------------------------

//CLASS - CProtUtil

 

export class CProtUtil

{

    constructor(){}

 

    static getUINT16toUINT8Arr(val)

    {

        let dat16 = new Uint16Array([val]);

        let dat8  = new Uint8Array(dat16.buffer);

        dat16     = null;

 

        return dat8;

    }

 

    static getBuffTrim(buf) //"getBuffTrim(.)"버퍼양옆의 빈값 제거

    {

        return BUFFTRIM.trim(buf);;

    }

 

    static getUINT8ArrToUINT16(uint8Arr)

    {

        let uint8buf = Buffer.from(uint8Arr);

        let uint16    = uint8buf.readUIntLE(0, uint8buf.length);

        uint8buf     = null;

 

        return uint16;

    }

 

    static getUINT8ArrToString(uint8Arr, encoding)

    {

        let iv  = new ICONV.Iconv(encoding, 'utf-8');

        let uint8buf = CProtUtil.getBuffTrim(Buffer.from(uint8Arr)); //"getBuffTrim(.)"버퍼양옆의 빈값 제거

        let str  = iv.convert(uint8buf).toString();

        uint8buf = null;

        iv          = null;

 

        return str;

    }

 

    static getStringToUINT8Arr(dat, encoding)

    {

        let iv  = new ICONV.Iconv('utf-8', encoding);

        let buf = Buffer.from(dat); //let strEncoding = JSCHARDET.detect(buf);

        let buf_cvt = iv.convert(buf);

        iv   = null;

        buf = null;

 

        return buf_cvt;

    }

 

    static convertStructType(struct, dat)

    {

        let tmpData = null;

        if(dat instanceof Uint8Array) //uint8array buf type

        {

            tmpData = struct.decode(dat, 0, {endian:"LE"});

        }

        else {

            tmpData = dat;

        }

 

        return tmpData;

    }

 

    static convertArrDecode(cls, arrDat, cnt, encoding) //class, arrData, cnt, encoding

    {

        let arr = new Array();

 

        let tmpInfo = null;

        for(let i=0; i<cnt; i++)

        {

            tmpInfo = arrDat[i];

 

            if(tmpInfo)

            {

                arr.push(cls.getDecode(tmpInfo, encoding));

            }

        }

 

        return arr;

    }

}

 

 

 

 

 

=================================

=================================

=================================

 

 

 

 

cpp-struct-js 를 ES6이상용으로 Class를 이용하여 응용해 보기(1)

(위에 js클래스 CProtUtil 를 이용해서 형변환 사용)

 

 

//ES6 이상 모듈 import, export 사용 아래와같이 node.js[npm] 에서(cpp-struct-js, iconv, node-buffertrim)을 인스톨해주고

//아래와 같이 코드에 import 로 추가해준다.

 

import STRUCTCPP from "cpp-struct-js";

import ICONV from "iconv";

import BUFFTRIM from "node-buffertrim";

 

 

//---------------------------------------------------------------------------------------------------------------

//CLASS -> CTTestA

 

//비공개 변수

let priCTTestA  = {

    _structObj: null,

};

export class CTTestA //"TestA", "TestB", "TestC"

{

    constructor(){}

 

    static get getStruct()

    {

        if(priCTTestA._structObj == null)

        {

            priCTTestA._structObj  = new STRUCTCPP("TTestA", [

                "TestA", STRUCTCPP.uint8(2),

                "TestB", STRUCTCPP.uint8(2),

                "TestC", STRUCTCPP.uint8(2),

            ]); //const SZ_TTestA = 6;

        }

        return priCTTestA._structObj;

    }

 

    static getDecode(dat, encoding)

    {

        let tmpData = CProtUtil.convertStructType(CTTestA.getStruct, dat);

 

        let TestA = CProtUtil.getUINT8ArrToUINT16(tmpData.TestA);

        let TestB = CProtUtil.getUINT8ArrToUINT16(tmpData.TestB);

        let TestC = CProtUtil.getUINT8ArrToUINT16(tmpData.TestC);

 

        return {TestA , TestB , TestC};

    }

}

 
 

//-------------------------------------------------------------------------------------------------------------------

//CLASS -> CTDBPsjPktTest 

 

//struct 값 "info" 에 클래스"CTTestA"의 struct 타입으로 넣는다.

 
let priCTDBPsjPktTest = {
    _structObj: null,
};
export class CTDBPsjPktTest //"PktTest1", "PktTest2", "info"
{
    constructor(){}
 
    static get getStruct()
    {
        if(priCTDBPsjPktTest ._structObj == null)
        {
            priCTDBPsjPktTest ._structObj  = new STRUCTCPP("TDBPsjPktTest", [
                "PktTest1", STRUCTCPP.uint8(2),
                "PktTest2", STRUCTCPP.uint8(2),
                "info", STRUCTCPP.type(CTTestA.getStruct, 20),
            ]); //const SZ_TDBPsjPktTest = 124;
        }
        return priCTDBPsjPktTest._structObj;
    }
 
    static getDecode(dat, encoding)
    {
        let tmpData = CProtUtil.convertStructType(CTDBPsjPktTest .getStruct, dat);
 
        let PktTest1= CProtUtil.getUINT8ArrToUINT16(tmpData.PktKind);
        let PktTest2= CProtUtil.getUINT8ArrToUINT16(tmpData.PktSize);
        let info = tmpData.info;
 
        return {PktTest1, PktTest2, info};
    }
 
    static getEncode(kind)
    {
        let bufSvrList = new Buffer.alloc(CTDBPsjPktTest.getStruct.size());
 
        CTDBPsjPktTest.getStruct.encode(bufSvrList, 0, {
            PktTest1: CProtUtil.getUINT16toUINT8Arr(kind),
            PktTest2: CProtUtil.getUINT16toUINT8Arr(CTDBPsjPktTest .getStruct.size()),
        },{endian: "LE"});
 
        return bufSvrList;
    }
}

 

 

=================================

=================================

=================================

 

 

 

cpp-struct-js 를 ES6이상용으로 Class를 이용하여 응용해 보기(2)

(위에 js클래스 CProtUtil 를 이용해서 형변환 사용)

 

 

//ES6 이상 모듈 import, export 사용 아래와같이 node.js[npm] 에서(cpp-struct-js, iconv, node-buffertrim)을 인스톨해주고

//아래와 같이 코드에 import 로 추가해준다.

 

import STRUCTCPP from "cpp-struct-js";

import ICONV from "iconv";

import BUFFTRIM from "node-buffertrim";

 

 

 

 

=================================

=================================

=================================

 

 

// CTDBIRec: //비공개 변수

 

export const T2O = 16;

 

let priCTestC = {

    _structObj: null,

};

export class CTestC  //test1str, test2str, test3uint, test4uint, test5uint

{

    constructor(){}

 

    static get getStruct()

    {

        if(priCTestC._structObj == null)

        {

            priCTestC._structObj = new STRUCTCPP("TDBIRec", [

                "test1str", STRUCTCPP.uint8(T2O), //String

                "test2str", STRUCTCPP.uint8(T2O), //String

                "test3uint", STRUCTCPP.uint8(2),  //uint16

                "test4uint", STRUCTCPP.uint8(2),  //uint16

                "test5uint", STRUCTCPP.uint8(2),  //uint16

            ]); // const SZ_CTestC = 38;

        }

        return priCTestC._structObj;

    }

 

    static getDecode(dat, encoding)

    {

        let tmpData = CProtUtil.convertStructType(CTDBIRec.getStruct, dat);

 

        let Addr = CProtUtil.getUINT8ArrToString(tmpData.Addr, encoding);

        let Name = CProtUtil.getUINT8ArrToString(tmpData.Name, encoding);

        let Flag = CProtUtil.getUINT8ArrToUINT16(tmpData.Flag);

        let NowU = CProtUtil.getUINT8ArrToUINT16(tmpData.NowU);

        let MaxU = CProtUtil.getUINT8ArrToUINT16(tmpData.MaxU);

 

        return {Addr, Name, Flag, NowU, MaxU};

    }

}

 

 

 

 

 

=================================

=================================

=================================

 

 

#기타관련링크

http://adrichman.github.io/nodejs-parsing-binary-from-the-tcp-layer/

 

https://stackoverflow.com/questions/51122724/reading-a-tcp-packet-on-node-js-server

 

https://www.npmjs.com/package/struct

 

- https://www.npmjs.com/package/cpp-struct-js

 

https://www.npmjs.com/package/c-struct-js

 

 

=================================

=================================

=================================

 

 

 

 

반응형


관련글 더보기

댓글 영역