블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2013. 1. 30. 16:03 NodeJS/Prototyping

한개의 Socket.io 인스턴스를 공유하여 3개의 독립적인 채팅 서버를 운영하는 방법과 Express.js 사용시 virtual host를 설정하여 sub domain을 만드는 방법을 알아본다.



1. 가정

  - mydomain.com / sub1.mydomain.com / sub2.mydomain.com 의 서로 독립적인 채팅서버 운영

  - socket.io 인스턴스는 하나만 사용하여 공유함

  - 테스트 사이트 구경하기



2. GitHub에서 소스 다운로드

  - git 기본 설치 되었음을 가정한다 (참조)

  - 각 분리된 namespace에서 Chat 애플리케이션 3개의 인스턴스를 수행할 것이다

  - 소스 설치하기 

[mongodb@localhost ~]$ git clone git://github.com/braitsch/sub.socket.io.git socket-io-example

Initialized empty Git repository in /home/mongodb/socket-io-example/.git/

... 중략 ..

Resolving deltas: 100% (56/56), done.

[mongodb@localhost ~]$ cd socket-io-example/

[mongodb@localhost socket-io-example]$ pwd

/home/mongodb/socket-io-example


///////////////////////////////////////////

// 각 분리된 namespace 존재 

[mongodb@localhost socket-io-example]$ ls 

README.md  

mydomain.com 

sub1.mydomain.com  

sub2.mydomain.com



3. mydomain.com의 app.js 와 mydomain.com/subdomain/sub1(2).js 소스이해

  - Express 사용하기 

// app.js

var exp = require('express');

var app = exp.createServer();


  - socket.io Listen 설정 : 전역(gobal)으로 Socket.IO 인스턴스에 접근하도록 선언한다 

// app.js

// create the single instance of socket.io that will be shared across all applications //

// 한개의 socket.io인스턴스를 생성하여 모든 애플리케이션간 공유한다

global.socket = require('socket.io').listen(app);

global.socket.set('log level', 1);

global.socket.set('transports', [ 'websocket', 'flashsocket', 'htmlfile', 'xhr-polling', 'jsonp-polling']);


  - subdomain 만들기

    + virtual host로써 동작하는 subdomain 애플리케이션을 설정한다

// create subdomain applications //

app.use(exp.vhost('sub1.' + global.host, require('./subdomains/sub1')));

app.use(exp.vhost('sub2.' + global.host, require('./subdomains/sub2')));


  - 모듈 로딩하면서 수행

// finally create this application, our root server //

// chat-socket.js 파일 모듈 로딩

require('./app/config')(app, exp);

require('./app/server/router')(app);

require('./app/server/modules/chat-socket');


  - sub1(2).js 소스 : 채팅 커넥션과 메세지를 다루기위해 chat-socket을 포함한 Express 애플리케이션이다

var exp = require('express');

var app = exp.createServer();


app.root = global.root + '/sub1.mydomain.com';

require(app.root + '/app/config')(app, exp);

require(app.root + '/app/server/router')(app);

require(app.root + '/app/server/modules/chat-socket');

module.exports = app;



4. chat-socket.js 소스이해

  - socket.io에 네임스페이스를 '/chat'으로 정의하기 

// pwd : ./socket-io-example/mydomain.com/app/server/modules

module.exports = function()

{

// 랜덤하게 사용자 색깔 지정 

var colors = ['#AE331F', '#D68434', '#116A9F', '#360B95', '#5F209E'];

        // 사용자 커넥션 추적을 유지하기 위한 객체 생성 

var connections = { };

// if you use socket.of(), you can set namespace. 

        // chat 네임스페이스 정의하고 connect  되면 수행할 펑션들 정의 

global.socket.of('/chat').on('connection', function(socket) {

       // give each connected user a random color so it's easier to tell them apart in the chat log //

               // 채팅사용자가 준비상태 

socket.on('user-ready', function(data) {

socket.name = data.name;

socket.color = data.color = colors[Math.floor(Math.random() * colors.length)];

brodcastMessage('user-ready', data);

});


               // 메세지 전송하기 

socket.on('user-message', function(data) {

data.color = socket.color;

brodcastMessage('user-message', data);

});


                // 접속자 상태 전송 

function dispatchStatus()

{

brodcastMessage('status', connections);

}

                // 메세지 전송하기 

function brodcastMessage(message, data)

{

               // remove socket.emit if you don't want the sender to receive their own message //

socket.emit(message, data);

socket.broadcast.emit(message, data);

}

                // soket.io 커넥션 끊길 때 호출 

        // handle connections & disconnections //

connections[socket.id] = {}; dispatchStatus();

socket.on('disconnect', function() {

delete connections[socket.id]; dispatchStatus();

brodcastMessage('user-disconnected', { name : socket.name, color : socket.color });

});

});

}();


  - sub1.mydomain.com 에 대하여 네임스페이스를 틀리게 기술한다 

global.socket.of('/chat-sub1').on('connection', function(socket) ...


  - sub2.mydomain.com 에 대하여 네임스페이스를 틀리게 기술한다 

global.socket.of('/chat-sub2').on('connection', function(socket) ...



5. index.js와 index.jade 소스이해

  - index.jade 하단에 index.js 와 socket.io.js 포함 

div.navbar.navbar-fixed-top

div.navbar-inner

div.container-fluid

ul.nav.pull-left

.brand Node.js & Socket.IO Chat Server

ul.nav.pull-right

li

a(href='#')#connected


#container

#send-message.well.span4

h2 Say Something

hr

label Your Name 

input(type="text", name="name", id='name').span4

label Your Message

textarea(rows='6', name="msg", id='msg').span4

button#btn-send.btn.btn-primary

i.icon-pencil.icon-white

| Send it !

#messages.well.span4

h2 Conversation

hr

#incoming


script(src='/vendor/jquery.min.js')

script(src='/socket.io/socket.io.js')

script(src='/js/index.js')


  - index.js 파일

$(document).ready(function() {


$('#msg').focus();

        // give user a generic name to start //

        // 사용자가 접속하면 랜덤 명칭을 설정한다 

$('#name').val(Math.random().toFixed(8).toString().substr(2));

$('#btn-send').click(function(){ sendMessage(); })

$('#msg').keypress(function(e){ if (e.keyCode === 13) { sendMessage(); return false; } })


       // initialize the socket connection to listen on the 'chat' namespace //

socket = io.connect('/chat');

socket.on('status', function (connections) {

var i=0; for (p in connections) i++;

var s = i > 1 ? ' are '+i+' People ' : ' is '+i+' Person ';

$('#connected').html('There '+s+' Currently Connected');

});

socket.on('user-ready', function (data) {

$('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: connected</span><br>');

autoScroll();

});

socket.on('user-message', function (data) {

$('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: '+ data.message+'</span><br>');

autoScroll();

});

socket.on('user-disconnected', function (data) {

$('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: disconnected</span><br>');

autoScroll();

});


        // register the user's name with the socket connection on the server // 

socket.emit('user-ready', {name : $('#name').val() });

var autoScroll = function() { 

document.getElementById('incoming').scrollTop = document.getElementById('incoming').scrollHeight; 

}


        // 메세지 보내기 

var sendMessage = function() {

socket.emit('user-message', {name : $('#name').val() , message : $('#msg').val() });

$('#msg').val('')

}

});



7. 채팅 서비스 설정하기 

  - 채팅 서비스에 대한 수행전에 필요한 모듈에 대해서 install 한다 

///////////////////////////////////////////////////////////////

// mydomain.com 에 stylus, jade, express, socket.io 설치

// package.json 참조하여 설치함

$ cd mydomain.com 

$ npm install -d 

... 중략 ...

stylus@0.32.0 node_modules/stylus

jade@0.28.1 node_modules/jade

express@2.5.8 node_modules/express

socket.io@0.9.13 node_modules/socket.io

npm info ok 


// mydomain.com 안에 모듈 설치 디렉토리 생성

[mongodb@localhost mydomain.com]$ ll
total 20
drwxrwxr-x. 4 mongodb mongodb 4096 Feb  1 23:17 app
-rw-rw-r--. 1 mongodb mongodb 1109 Feb  1 23:17 app.js
drwxrwxr-x. 7 mongodb mongodb 4096 Feb  2 19:49 node_modules
-rw-rw-r--. 1 mongodb mongodb  590 Feb  1 23:17 package.json
drwxrwxr-x. 2 mongodb mongodb 4096 Feb  1 23:17 subdomains

//////////////////////////////////////////////////////
// sub1/sub2 도메인에 jade, stylus, express 설치 
// (socket.io는 mydomain에만 설치됨)
$ cd ../sub1.mydomain.com
$ npm install -d 
... 중략 ...
npm info prepublish sub.socket.io@1.0.0
jade@0.28.1 node_modules/jade
stylus@0.32.0 node_modules/stylus
express@2.5.8 node_modules/express


  -  /etc/hosts 파일에 다음을 설정한다 : 브라우져에서 호출하기 위한 sub1, sub2 도메인을 설정한다 

127.0.0.1 sub1.localhost

127.0.0.1 sub2.localhost



8. Chat Node Server 실행하기

  - mydomain.com 디렉토리로 들어가서 Node를 실행한다

/home/mongodb/socket-io-example/mydomain.com

[mongodb@localhost mydomain.com]$ node app

   info  - socket.io started

Express server listening on port 8080 in development mode 


  - 브라우져 2개를 띄워서 http://localhost:8080 을 동일하게 실행한다 

    + dowon 채팅창과 다른 브라우져창에서 try 명칭의 사용자가 들어왔다

    + socket.io를 통하여 채팅을 주고 받고 있다


  - 브라우져 2개를 띄워서 http://sub1.localhost:8080 을 동일하게 실행한다 (http://sub2.localhost:8080 동일 수행함)

    + youngsik 채팅창과 다른 브라우져창에서 girl 명칭의 사용자가 들어왔다

    + mydomain.com의 동일한 socket.io를 통하여 채팅을 주고 받고 있다 (namespace만 틀릴 뿐이다)



<참조>

  - 원문 : Building a Node.js Chat Application and Sharing Socket.IO Across Multiple Subdomains

  - 소스 : https://github.com/braitsch/sub.socket.io

  - Express.js Virtual Host 설정

  - 원문과 동일한 socket.io 인스턴스 한개와 Virtual Host 를 통한 SubDomain 공유에 대한 동일 예제


posted by 윤영식