관리 메뉴

가끔 보자, 하늘.

c++ 와 js간 소켓 통신 예제 + α 본문

개발 이야기/개발 및 서비스

c++ 와 js간 소켓 통신 예제 + α

가온아 2021. 7. 6. 17:47

Legacy 프로젝트를 모바일 혹은 웹으로 확장 할 일이 있어 겸사 겸사 정리해 보았습니다. 

 

샘플 프로젝트는 아래와 같은 흐름을 가진다고 가정합니다. 모든 연결은 stateful로 연결되어 필요한 경우 수시로 데이터를 주고 받을 수 있게 합니다. 

 

최종 사용자는 제공된 웹 앱 혹은 모바일 앱에서 wss로 javascript server에 연결하여 인증과 기타 필요한 작업을 요청합니다. 그리고 javascript server는 VPC 내부의 서버에 socket으로 연결합니다. c++ server 입장에서는 javascript server를 위해 준비할 사항은 추가적인 패킷 정의 뿐입니다. 

 

이제 실제 샘플을 실행해 보겠습니다. 

 

아래 예제에 사용된 코드는 이 곳에서 다운로드 가능합니다. 

 

C++ 서버 준비

우선 c++ winsock으로 제작된 간단한 에코 서버를 준비하세요. 예제 소스 중 "cpp-server.cpp" 파일을 사용하시면 됩니다.이 파일은 vs2008 버전에서도 정상적으로 작동합니다. 빈 win32 콘솔 프로젝트를 생성 후 다운받은 코드를 프로젝트에 적용, 빌드하시면 됩니다.

 

예제 서버에서는 js에서 전달된 메세지는 콘솔 화면에 출력하고 미리 정의된 간단한 구조체 형태의 자료를 전달하는 역할을 합니다.

 

js로 전달할 내용의 구조체는 아래와 같이 선언하여 c++에서 전달될 integer와 string을 js에서 어떻게 파싱해서 받을지 같이 확인해 보겠습니다.

struct cPacket_Test {
	int	point;
	char	strLength32[32];
};

보통 실무에서는 protobuf를 사용하시겠지만, js에서 buffer를 다루는 방법을 익혀볼 겸 raw data 형태로 전달해 보겠습니다.

 

Javascript 서버 준비

예제 소스 중  "js-server.js"파일을 사용하시면 됩니다. nodejs로 실행하시면 되며, 필요한 모듈은 https, JSON, fs, net, websocket입니다.

 

보안을 위해 wss를 사용할 것을 추천합니다. 이를 위해 private key와 인증서가 필요합니다. 테스트를 위해 사설 인증서를 사용하실 분은 이 글을 참고하셔서 생성하시면 됩니다.

.
.
// ssl 설정을 위한 pem 파일 설정
const sslOptions = {
	key : fs.readFileSync("./your_private_key.pem"),
	cert: fs.readFileSync("./your_cert.pem")
};
.
.
    client.on('data', (data) => {
    	// c++ 서버에서 전달된 구조체는 아래와 같이 처리합니다.
        var intb = new Buffer.alloc(4);
        var strb = new Buffer.alloc(32);

        intb = data.readInt32LE(0);
        //	32byte 중 string 뒤에 비어 있는 부분은 이와 같이 버릴 수 있습니다.
        strb = data.toString("utf8",4,36).split('\u0000')[0];
        console.log(intb, strb)
        //	json format으로 클라이언트로 전달합니다.
        connection.sendUTF('{"int" : '+intb+', "str": "'+strb+'"}');
    });
.
.
    // 클라이언트에서 받은 내용을 바로 전달한다.
    connection.on('message', (msg) => {
        console.log( msg )
        client.write(msg.utf8Data);
    });

c++ server에서 data 전송 시 binary 형태로 데이터가 전달되며 js에서는 전달하거나 받을 자료 구조를 미리 확인 후 알맞게 파싱하여 사용해야 합니다. 

 

위 c++ server에서 보내는 cPacket_Test를 보시면 앞 4byte는 integer, 뒤 32byte는 string으로 된 것을 확인할 수 있습니다. 

 

이를 위해 Buffer.alloc으로 buffer를 생성 후 전달된 binary 데이터인 data 에서 integer와 string을 파싱합니다. 

 

코드 중 "data.readInt32LE(0)" 은 전달된 binary 데이터인 "data"에서 0바이트부터 int32(4byte)를 LE(Little Endian - Byte Order 형식입니다.) 형식으로 읽어옵니다. 

 

코드 중 "data.toString("utf8",4,36).split('\u0000')[0];"은 전달된 binary 데이터인 "data"에서 앞에서 읽은 4byte를 제외하기 위해 4byte부터 32byte를 읽어 스트링으로 변환하고 c++ 에서 char strLength32[32];로 정의된 데이터 중 실제 사용된 스트링 뒤에 null 초기화 부분을 분리해서 실제 string만을 strb에 기록합니다.

 

이 샘플에서는 c++ server로 클라이언트에서 받은 바로 전달하는 과정으로 제작되어 있으나 실제로는 c++ server에서 사용하는 포멧을 보내야 할 것입니다. 

 

위 샘플 코드처럼 string을 보내지 않고 cPacket_Test를 생성해 보낸다고 가정한다면 아래와 같이 작성할 수 있습니다. 

    // cPacket_Test 패킷 포멧으로 메세지를 전달한다고 가정해 보겠습니다.
    connection.on('message', (msg) => {
        let buffer  = new ArrayBuffer(36);  // 4 + 32 byte. c++ server cPacket_Test 참고.
        let dataview = new DataView(buffer);
        
        dataview.setInt32(0, 99,true);
        var buffer_pos = 4;
        for(let p=0;p<32;p++){  // cPacket_Test의 경우 char를 포함합니다. wchar를 사용할 경우 2byte씩 기록하시면 됩니다.
            if(si>= _name.length){
                dataview.setInt8(di,0);     // 문자열 길이를 넘어선 부분은 null로 채워주고 작업을 종료합니다.
                break;
            }else{
                let charcode = _name.charCodeAt(si);
                dataview.setInt8(di,charcode);
            }
            di+=1;
        }
        var packet = new Buffer.from(buffer);
        client.write(packet);
    });

 

Client 준비

 

client는 html 파일을 사용합니다. 예제 소스 중  "client.html"파일을 참고하세요.

 

처음 로딩 시 wss://localhost:3000으로 접속하고, 화면의 "send" 버튼을 누르면 텍스트를 javascript server는 이를 c++ server로 전송 후 cPacket_Test 포멧의 데이터를 전달받아 파싱 후 client에게 다시 데이터를 전달합니다. 

 

.
.
    function onMessage(evt)
    {
    	//	js server에서 전달된 데이터를 json으로 parsing 후 string 부분을 화면에 출력해 봅니다.
        writeToScreen('<span style="color: blue;">수신: ' + JSON.parse(evt.data).str+'</span>');
    }
.
.

 

샘플 코드들은 불필요한 부분을 최소화해 필요한 부분만 간단히 볼 수 있도록 업로드 했습니다. 

 

(* 클라이언트 소스는 이 블로그에서 발췌하여 일부 수정했습니다.)

 

 

이런 식으로 서비스를 구성할 프로젝트가 별로 없을 듯 하지만 혹시 필요하신 분이 있을지도 몰라 공유해 봅니다. 

미래의 저를 위해서도... ^^a

 

 

 

 

 

 

반응형