새로운 강의는 이제 https://memi.dev 에서 진행합니다.
memi가 Vue & Firebase로 직접 만든 새로운 사이트를 소개합니다.
Communication(통신) 3. 구현-기본
간단하게 프로토콜을 만들어보았다. 이제 정의된 프로토콜 대로 구현해보도록 하겠다..
준비
클라이언트
테스트를 하려면 소켓 접속,종료 바이너리 데이터를 보내고 받을 수 있는 어플리케이션이 있어야한다..
윈도우 프로그램이나 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 뭔지 적나라하게 보일 것이다..
댓글남기기