프로그래머...

필고 개발자그룹에서는
필리핀에 계시는 개발자분들이 함께 성장 할 수 있는 공간입니다.

정모: 11월 1일
Image at ../data/upload/7/1203907Image at ../data/upload/4/1112674Image at ../data/upload/2/1046612Image at ../data/upload/9/838729Image at ../data/upload/6/820796Image at ../data/upload/9/766049Image at ../data/upload/0/766040Image at ../data/upload/5/746745
Sub Page View
Today Page View: 5
Yesterday View: 22
30 Days View: 322
Image at ../data/upload/3/2288953

채팅서버 - 홈페이지에 채팅 프로그램을 만들어 넣자 !! - X Server (Node JS)(5)

Views : 10,947 2013-09-28 15:01

QR 스캔해주세요.
필고 개발자 그룹 1269556675
Report List New Post
C/S 개발을 처음 시작한지가 언제쯤일까요? Client 는 처음 개발을 시작 할 때 부터 작성하지만 Server 쪽은 경륜이 좀 있어야 제대로 개발을 할 수 있습니다.

주로 웹서버, 메일서버, 기타 간단한 자체 프로토콜 서버 등을 개발했었는데 한 15년 정도 전문적인 개발 업무를 중단하고 조금씩 취미로 진행하지 않다 보니 최근에는 힘에 많이 부치는 것 같습니다.

1인 프로젝트라는 말을 많이 하죠. 특히 개발 쪽에서 1인 프로젝트를 완성도있게 진행 하려면 많은 지식이 필요합니다.

홈페이지 만드는데 홈페이지 만드는 방법만 알면되지, 웹서버를 직접 만들 필요가 있을까요?

당연히 만들면 좋죠. 아주 잘 만들면 더할 나위가 없죠.

하지만 수십명의 서버 전문 프로그래머가 몇 년을 작업해도 제대로 된 웹 서버를 개발하기 어려운데 혼자서 그것을 어떻게 만들겠어요 라고 생각하면 ... 글쎄요... 라고... 대답을 ...


자체 개발 웹 서버로 홈페이지를 만들어서 서비스한다는 것이 얼마나 자랑스러운 일이겠습니까. 장점도 많죠. 세상에 하나뿐인 서버이고 소스 공개가 되지 않아서 외부로 부터의 침입을 편리하게 차단 할 수 있죠. 각종 커스터마이징을 통해서 손 쉽게 원하는데로 요리를 할 수 있습니다.


그래서 ... 웹서버를 만든 것이 아닙니다..... ??

약간의 보조 기능이 필요한데, 그리고 그러한 기능은 아주 잘 만들어진 세계 최고의 안전과 성능이 보장된 무료 오픈 소프트웨어가 있는데도 불구하고 그냥... 사용하기 귀찮아서 만들버렸습니다.

이름하여 XS ( Center X Server 의 약자 ) 입니다.

XS 서버는 Node 로 만들어져 있으면 아주 간결하게 코드가 짜여져 있습니다.
단순함이 최고의 소프트웨어라고 생각을 합니다.

XS 는 몇 줄 안되는 소스코드로 이루어져 있지만 웹서버, 채팅서버, API 서버 3 가지 기능을 포함하고 있습니다.


다음은 XS 실행하는 장면입니다.

2013-09-28 14-45-07_~.png


다음 XS 의 소스코드입니다.

---------------- 소스 코드 -----------------


/// docs.google.com/a/withcenter.com/document/d/1joigYA_4ZPB_I_IBkrNlS7sIKZCxEp7tWyd5ayzHJYA/edit#
var port = 8083;
http    = require("http").createServer(callback_http_request).listen(port),
path    = require("path"),
url    = require("url"),
fs    = require("fs"),
io    = require('socket.io').listen(http),
util = require('util'),
mime    = require('mime');    

console.log("Server Running on "+port);
io.set('log level', 2);
io.sockets.on('connection', on_connect);


function callback_http_request(request,response) {
    var pathname = url.parse(request.url).pathname;
    /** abc.com 또는 abc.com/dir/ 와 같이 접속하는 경우 해당 경로의 index.html 이 로드되도록 한다.
     */
    var p = //$/;
    if ( pathname == '/' ) pathname = '/index.html';
    else if ( p.test(pathname) ) {
        pathname = pathname + 'index.html';
    }
    
    var full_path = path.join(process.cwd() + '/www' ,pathname);
    console.log("pathname: " + pathname + " [ full_path : " + full_path + " ]");
    
    path.exists(full_path,function(exists){
        if(!exists){
            response.writeHeader(404, {"Content-Type": "text/plain"});
            response.write("404 Not Foundn");
            response.end();
        }
        else{
            var mimetype = mime.lookup(full_path);
            fs.readFile(full_path, "binary", function(err, file) {
                if(err) {
                    response.writeHeader(500, {"Content-Type": "text/plain"});
                    response.write(err + "n");
                    response.end();
                }    
                else{
                    response.writeHeader(200, {"Content-Type": mimetype});
                    response.write(file, "binary");
                    response.end();
                }
            });
        }
    });
}


function on_connect(socket)
{
    console.log('on_connect:'+socket);
    
    
    //socket.on('chat name', function(data){chat.name(socket,data);});
    //socket.on('chat message', function(data){chat.message(socket,data);});
    
    
    socket.on('chat', function(data) {
        chat(socket, data);
    });
    socket.on('api', function(data) {
        api(socket, data);
    });
    socket.on('anything', function(data) {
        io.sockets.emit('anything', { 'code' : -1001 } );
    });
    
}



function api(socket, data)
{
    console.log("api: post_id="+data.post_id);
    var http = require('http');
    var options = {
        host: 'philgi.org',
        port: 80,
        path: '/?module=post&action=list_json&theme=N&post_id=' + data.post_id
    };

    callback = function(response) {
        console.log('callback:');
        var str = ''
        response.on('data', function (chunk) {
            str += chunk;
        });
        response.on('end', function () {
            console.log(str);
            socket.emit( 'api-forum-list', { 'list': str } );
        });
    }
    var req = http.get(options, callback);
}


/*********************************************************************
 *
 * Chatting Protocol...
 *
 */
var chat_info = {};
var chat_socket_id = {};
function chat(socket, data)
{
    console.log( "from client: " + util.inspect(data) );
    var client = get_chat_info(socket, data);
    if ( ! client ) {
        return;
    }
    /** 처음 접속인가?
     * 주의 : 재 접속 또는 페이지 이동을 할 때에도 이 함수가 호출 되어야 한다.
     * 만약, 재 접속이라면 서버에 아이디가 기록되어져 있다. 이전에 접속이 되어 정보가 있는 상태라면,
     * 즉, (대기실이라도) 방이 있는 상태라면 방으로 입장을 한다.
     */
    if ( data.action == 'enter' ) {
        console.log("Emitting to '"+client.room+"' : enter" );
        /** 처음 입장하면 lobby 가 되고 원래 자기 방이 있으면 해당 방으로 브로드 캐스팅 한다.
         *
         */
        chat_room_enter( socket, client.id, client.room ); /// enter 할 때마다 방 입장을 한다. 동일한 방이라도 계속 입장한다.
        data.room = client.room; // 입장한 사용자의 방 이름을 전달. 대기실이면 lobby
        io.sockets.in(client.room).emit('chat', data);
    }
    else if ( data.action == 'message' ) {
        //io.sockets.emit('chat', data);
        console.log("Emitting to '"+client.room+"' : " + data.value);
        data.name = client.name;
        io.sockets.in(client.room).emit('chat', data);
    }
    else if ( data.action == 'join' ) {
        console.log(client.id + ' leaves the room : ' + client.room);
        socket.leave(client.room);
        io.sockets.in(client.room).emit('chat', {action: 'leave', id: client.id, name: client.name, 'to': data.value} ); /* 퇴장 브로드 캐스팅 */
        chat_room_enter( socket, client.id, data.value );
        io.sockets.in(data.value).emit('chat', {action: 'join', id: client.id, name:client.name, value:data.value} ); /* 입장 브로드 캐스팅 */
    }
    else if ( data.action == 'room list' ) {
        console.log("room list:");
        
        var connected = new Array();
        var entered = new Array();
        var users = io.sockets.manager.rooms[''];
        for ( var u in users ) {
            var sid = users[u];
            var id = chat_socket_id[sid];
            var c = chat_info[id];
            if ( c ) {
                console.log("FOUND socket id:" + sid + " User ID:" + c.id);
                entered.push(c);
            }
            else {
                connected.push(sid);
            }
        }
        console.log( util.inspect( io.sockets.manager.rooms[''] ) );
        console.log("CONNECTED but not entered:");
        console.log( util.inspect( connected) );
        console.log("CONNECTED and entered:");
        console.log( util.inspect( entered) );
        
        socket.emit('chat', { action: 'room list', 'connected': JSON.stringify(connected), 'entered': JSON.stringify(entered)});
    }
}
/** 모든 액션에서 이 함수를 통해서 사용자 정보를 얻는다.
 * 즉, 어떤 액션이든 이름을 변경 할 수 있다.
 */
function get_chat_info(socket, data)
{
    if ( typeof data.id == 'undefined' ) {
        console.log("ERROR: WRONG PROTOCOL");
        return {};
    }
    var id = data.id;
    var sid = socket.id;
    console.log("get_chat_info("+id+")");
    //console.log(util.inspect(chat_info));



    if ( chat_info[id] ) {
        console.log("chat info exists");
        chat_info[id].socket_id = sid; /// 재 접속할 때마다 socket.id 가 변경 될 수 있으므로 반드시 업데이트를 해야 한다.
    }
    else {
        console.log("chat info does not exists");
        chat_info[id] = {};
        chat_info[id].id = data.id;
        chat_info[id].socket_id = sid;
        chat_info[id].name = data.name;
        chat_info[id].room = 'lobby';
        /** 처음 접속시 대기실로 자동 입장 */
        chat_room_enter( socket, id, chat_info[id].room );
    }
    /// enter 할 때에는 사용자 이름을 업데이트 한다.
    if ( data.action == 'enter' ) {
        chat_info[id].name = data.name;
    }
    console.log( util.inspect( chat_info[id] ) );
    chat_socket_id[sid] = id;
    return chat_info[id];
}
function chat_room_enter( socket, id, room )
{
    socket.join(room);
    chat_info[id].room = room;
    console.log(id + ' joins ' + room);
}

  본 글을 신고하시겠습니까?
Report List New Post
오늘도잠온다 [쪽지 보내기] 2013-09-28 22:01 No. 1269556901
논외 이야기데 .. 단순하게 물어보는건대요 . 혹시 node js 사용 하실떄 소스를 통으로 올려서 서버 돌리시나요 아니면 빌드해서 서버에 올리시나요 ........... 전 c , C# , python 으로 서버 짤떄는 ...빌드 해서 올려는데 ..  node js 는 빌드 하는 방법을 잘모르겠어서 따로 필드 안하고 그양 소스를 통으로 올리시나요 ?
thruthesky [쪽지 보내기] 2013-09-29 00:09 No. 1269556962
@ 오늘도잠온다 님에게...C 의 컴파일과 오브젝트 파일을 링크 및 실행 파일의 생성 등의 과정이 필요합니다. 이러한 과정을 빌드라고 하죠.바이너리 실행 파일을 생성하는 또는 가상 실행 환경에 맞는 이미지 파일을 생성하는 언어의 경우에는 빌드 과정이 필요합니다.자바스크립트는 그냥 자바스크립트일 뿐입니다. 빌드라는 과정이 없습니다.NODE 는 그냥 자바 스크립트일 뿐입니다. 다만 실행이 웹브라우저에서 되지 않고 서버에서 됩니다.웹브라우저에서 실행하려면 HTML 소스 코드에 추가하면 되죠?서버에서 실행하려면 "node 자바스크립트-파일-이름" 으로 하면 서버에서 자바스크립트가 그냥 실행됩니다.자바스크립트는 인터프리터언어이며 소스 코드를 컴파일 할 필요 없이 텍스트 형태그대로 실행됩니다.
위세너
Clark Pampanga
http://philgo.com
오늘도잠온다 [쪽지 보내기] 2013-09-29 01:24 No. 1269556979
@ thruthesky 님에게...음 그건 아는데 ... 인터 프리터 언어도 조금 셋팅하면 exe 파일로 만들어서 배포 할수 있어서 혹시 다른 방법이 있나 싶어서 .. 물어본거에요 ..  답변 감사 합니다
thruthesky [쪽지 보내기] 2013-09-29 13:51 No. 1269557232
@ 오늘도잠온다 님에게...네, 베이직이나 PHP 등도 exe 파일로 만들 수있죠.자바스크립트에는 아직 바이너리 실행 파일을 만드는 것은 못보았습니다. 그도 그럴 것이 자바스크립트가 바이너리 실행 파일이 된다면 HTML 표준과 웹브라우저 진영에서 가만히 있리가 없죠. 하지만 HTML5 에는 그런 움직임이 있습니다.그리고 자바스크립트에서는 주로 Minifize 시키는 데, base64 로 인코딩을 하게 되면 소스코드 해독이 거의 불가능합니다.따라서 소스를 숨기려면 얼마든지 숨길 수 있습니다.참고로 위에 있는 소스코드를 base64 로 인코딩 한 것입니다. 해독하기를 아주 어렵게 만들어 거의 포기하는 수준으로 몰고 가는 것입니다.eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('b'+e(c)+'b','g'),k[c]);return p}('3 G=1H;E=o("E").1B(1b).10(G),y=o("y"),C=o("C"),P=o("P"),9=o('13.9').10(E),r=o('r'),Q=o('Q');1.0("1L 1G t "+G);9.1z('0 1A',2);9.q.t('2a',V);5 1b(e,f){3 g=C.1E(e.C).Z;3 p=//$/;8(g=='/')g='/19.18';m 8(p.1K(g)){g=g+'19.18'}3 h=y.D(22.1s()+'/1t',g);1.0("Z: "+g+" [ 1w : "+h+" ]");y.L(h,5(c){8(!c){f.R(17,{"T-U":"16/15"});f.J("17 1m 1qn");f.H()}m{3 d=Q.1x(h);P.1y(h,"12",5(a,b){8(a){f.R(1C,{"T-U":"16/15"});f.J(a+"n");f.H()}m{f.R(1D,{"T-U":d});f.J(b,"12");f.H()}})}})}5 V(b){1.0('V:'+b);b.t('k',5(a){k(b,a)});b.t('z',5(a){z(b,a)});b.t('11',5(a){9.q.s('11',{'1M':-1N})})}5 z(d,e){1.0("z: B="+e.B);3 f=o('E');3 g={1Q:'1R.1V',G:1W,y:'/?1Y=1Z&l=23&29=N&B='+e.B};X=5(b){1.0('X:');3 c=''1n.t('1o',5(a){c+=a});b.t('H',5(){1.0(c);d.s('z-1p-w',{'w':c})})}3 h=f.1r(g,X)}3 6={};3 W={};5 k(a,b){1.0("1u 1v: "+r.A(b));3 d=O(a,b);8(!d){M}8(b.l=='K'){1.0("14 I '"+d.4+"' : K");F(a,d.7,d.4);b.4=d.4;9.q.x(d.4).s('k',b)}m 8(b.l=='1F'){1.0("14 I '"+d.4+"' : "+b.v);b.j=d.j;9.q.x(d.4).s('k',b)}m 8(b.l=='D'){1.0(d.7+' 1I 1J 4 : '+d.4);a.1a(d.4);9.q.x(d.4).s('k',{l:'1a',7:d.7,j:d.j,'I':b.v});F(a,d.7,b.v);9.q.x(b.v).s('k',{l:'D',7:d.7,j:d.j,v:b.v})}m 8(b.l=='4 w'){1.0("4 w:");3 e=Y 1c();3 f=Y 1c();3 g=9.q.1d.1e[''];1P(3 u x g){3 h=g[u];3 i=W[h];3 c=6[i];8(c){1.0("1S 13 7:"+h+" 1T 1U:"+c.7);f.1f(c)}m{e.1f(h)}}1.0(r.A(9.q.1d.1e['']));1.0("1g 1X 1h S:");1.0(r.A(e));1.0("1g 20 S:");1.0(r.A(f));a.s('k',{l:'4 w','21':1i.1j(e),'S':1i.1j(f)})}}5 O(a,b){8(24 b.7=='25'){1.0("26: 27 28");M{}}3 c=b.7;3 d=a.7;1.0("O("+c+")");8(6[c]){1.0("k 1k L");6[c].1l=d}m{1.0("k 1k 2b 1h L");6[c]={};6[c].7=b.7;6[c].1l=d;6[c].j=b.j;6[c].4='2c';F(a,c,6[c].4)}8(b.l=='K'){6[c].j=b.j}1.0(r.A(6[c]));W[d]=c;M 6[c]}5 F(a,b,c){a.D(c);6[b].4=c;1.0(b+' 1O '+c)}',62,137,'log|console||var|room|function|chat_info|id|if|io||||||||||name|chat|action|else||require||sockets|util|emit|on||value|list|in|path|api|inspect|post_id|url|join|http|chat_room_enter|port|end|to|write|enter|exists|return||get_chat_info|fs|mime|writeHeader|entered|Content|Type|on_connect|chat_socket_id|callback|new|pathname|listen|anything|binary|socket|Emitting|plain|text|404|html|index|leave|callback_http_request|Array|manager|rooms|push|CONNECTED|not|JSON|stringify|info|socket_id|Not|response|data|forum|Found|get|cwd|www|from|client|full_path|lookup|readFile|set|level|createServer|500|200|parse|message|Running|8083|leaves|the|test|Server|code|1001|joins|for|host|philgi|FOUND|User|ID|org|80|but|module|post|and|connected|process|list_json|typeof|undefined|ERROR|WRONG|PROTOCOL|theme|connection|does|lobby'.split('|'),0,{}))
위세너
Clark Pampanga
http://philgo.com
오늘도잠온다 [쪽지 보내기] 2013-09-29 21:38 No. 1269557460
@ thruthesky 님에게...오 이런 방법도 있군요  좋은 정보 감사 합니다
필고 개발자 그룹
No. 95
Page 3
권오창@페이스...  3224  19-04-07
만강이  2860  15-08-31
Photo Post thumbbail image
whizenglish  4309  15-06-20
Post thumbnail image
whizenglish  7196  15-05-21
커먼웰쓰  3479  15-05-14
바다디다  2591  15-02-21
dnfkfkfkfkf  3464  15-01-25
필고관리자  2991  15-01-03
Post thumbnail image
필고관리자  9941  15-01-03
굿리  3395  15-01-02
요잇  3259  14-12-11
jeremyPark  3053  14-12-03
Post thumbnail image
thruthesky  39687  14-09-30
필고관리자  3444  14-09-30
오늘도잠온다  3776  14-05-03
오늘도잠온다  3375  14-04-07
필고관리자  4233  14-01-15
thruthesky  10564  14-01-11
thruthesky  5612  13-12-22
Post thumbnail image
thruthesky  6819  13-12-16
thruthesky  10269  13-11-26
thruthesky  3825  13-11-24
thruthesky  2184  13-11-10
thruthesky  2557  13-11-10
thruthesky  4845  13-11-10
thruthesky  3689  13-11-10
thruthesky  2698  13-09-29
Post thumbnail image
thruthesky  7972  13-09-28
Post thumbnail image
thruthesky  10948  13-09-28
오늘도잠온다  4605  13-09-27
오늘도잠온다  2133  13-09-27
Photo Post thumbbail image
필고관리자  10219  13-09-21
thruthesky  2790  13-09-15
Photo Post thumbbail image
thruthesky  5836  13-09-12
Photo Post thumbbail image
thruthesky  2574  13-09-11
Photo Post thumbbail image
thruthesky  12933  13-09-10
Photo Post thumbbail image
thruthesky  12162  13-09-08
Photo Post thumbbail image
thruthesky  5245  13-09-07
thruthesky  3951  13-09-03
깜씨  3561  13-09-02