Communication(통신) 3. 구현-기본

2 분 소요

간단하게 프로토콜을 만들어보았다. 이제 정의된 프로토콜 대로 구현해보도록 하겠다..

준비

클라이언트

테스트를 하려면 소켓 접속,종료 바이너리 데이터를 보내고 받을 수 있는 어플리케이션이 있어야한다..

윈도우 프로그램이나 node webkit으로 10줄 정도면 만들지만 포스팅 에너지가 많이 들어가므로 알아서 다운받든 만들든 해서 쓸 툴이 준비되어있어야한다..(시간될 때 추후에 한번 다뤄보겠다 얼마나 쉬운지..)

서버

바이너리 프로토콜이니 우선 어떤 데이터가 들어오는 지 바이트를 확인 할 수 있는 루틴이 제일 중요하다..

node.js의 net module : https://nodejs.org/dist/latest-v6.x/docs/api/net.html 을 사용해 구현해보겠다..

실제로 작업에 들어갈 때는 구글링(스택오버등) 하기전에 항상 레퍼런스 사이트를 정독하자.. 아래코드도 레퍼런스 대로 추가했을 뿐 절대 의미 없는 코드를 만들지 않는다…
이해가 중요한 것이다 C++, java, python, ruby 도 똑같이 구현해보면 된다..

접속

우선 연결 유무를 판단해야한다..

var net = require('net');
net.createServer(function(sock) {
    console.log('open : ' + sock.remoteAddress);
    
    // Add a 'close' event handler to this instance of socket
    sock.on('close', function() {
        console.log('close : ' + sock.remoteAddress);
    });
}).listen(1234);

localhost:1234로 socket을 open 해놓고 리스닝 중인 것이다.

누군 가 접속을 하면 ‘open + #.#.#.#’ 이 뜰 것이고

소켓을 닫으면 ‘close + #.#.#.#’ 이 뜰 것이다.

에러 처리

그런데 갑자기 의도치않게 소켓 연결 에러가 나버리면? 서버는 리셋되게 되어 있다.

이벤트 핸들러를 정해주지 않아 갈곳을 잃었기 때문..

그러니 에러 핸들러를 추가해주자..

net.createServer(function(sock) {    
    console.log('open : ' + sock.remoteAddress);        
    sock.on('close', function() {
        console.log('close : ' + sock.remoteAddress);
    });
    sock.on('error', function(err){
        console.error('error : ' + sock.remoteAddress + ' / ' + err);
    });    
}).listen(1234);

데이터 수신

대부분의 서버는 chunk 즉 바이트 덩어리로 무작위 길이로 날아온다..

net.createServer(function(sock) {    
    console.log('open : ' + sock.remoteAddress);        
    sock.on('close', function() {
        console.log('close : ' + sock.remoteAddress);
    });
    sock.on('error', function(err){
        console.error('error : ' + sock.remoteAddress + ' / ' + err);
    });    
    sock.on('data', function (data) {
        console.log(data);
    });
}).listen(1234);

client에서 ‘ABCD’ 를 날려보자..

이정도는 unix 계열이라면 nc localhost 1234 정도로 테스트 해도 된다.

[0x41,0x42,0x43,0x44] 의 버퍼형 데이터가 올 것이다..

노드의 버퍼 시스템으로는 Buffer.from([0x41,0x42,0x43,0x44]) 과 내용이 같다..

참고 node buffer : https://nodejs.org/dist/latest-v6.x/docs/api/buffer.html

위 링크만 대충 봐도 바이트와 스트링과의 관계를 이해할 수 있다.
C++, java, python 서버소켓도 만들어봤지만 당연히 같은 원리로 들어온다…

들어올 데이터가 스트링인 것을 알고 있다면?

    sock.on('data', function (data) {
        console.log(data.toString());
    });

이렇게 함으로써 “ABCD” 임을 확인 할 수 있다.

데이터 량이 많아질 경우 “…” 처리가 되고 c등의 원시적인 소켓 모듈에서는 버퍼를 표현(스트링으로 치환해야함)하기가 힘들 수 있으므로 범용적으로 쓸 수 있는 String을 만들어본다.

net.createServer(function(sock) {    
    sock.on('data', function (data) {
        //console.log(data);
        let len = data.length; //중간에 데이터가 들어오기 때문에 아래 for에서 직접쓰면 오류가 날수 있으므로 길이를 적어둔다
        let hs = '';
        for(let i = 0; i < len; i++) hs += '0x' + data[i].toString(16) + ' ';
        console.log(hs);        
    });
}).listen(1234);

이제 ABCD 를 보내면 0x41 0x42 0x43 0x44 로 확인이 된다.

각 언어별 헥스스트링 method 찾아서 data[i].toString(16) 부분을 바꾸면 된다..
eg) c++ : IntToHex(data[i],2)

보낼 때도 써야하니 함수로 만들어 놓는 것이 좋겠다..

function logh(d) {
    let hs = '';
    let l = d.length;
    for(let i = 0; i < l; i++) hs += '0x' + d[i].toString(16) + ' ';
    return hs;   
}

결론

바이너리던 스트링이던 html이던 json이던 xml이든 이제 모든 데이터는 헥스 스트링으로 판단할 수 있게 되었다.

다음에는 그 어떤 데이터가 오더라도 뻥뻥 터지지 않는 서버를 위해 한바이트 한바이트 소중히 해석하며 처리할 수 있는 서버를 만들어보겠다..

수신서버는 방화벽이 애니오픈이어야할 경우기 때문에 더더욱 한땀 한땀 체크하는게 당연한 것이다..

http packet이 궁금하지 않은가? 브라우저에서 http://localhost:1234 접속해보면..
0x47 0x45 0x54 0x20 0x2F 0x20 0x48 0x54 0x54 … http protocol 뭔지 적나라하게 보일 것이다..

댓글남기기