Basic chat program in Node.js

TCP/IP 프로토콜 상에서 통신하는 기본적인 채팅 프로그램을 만들었다. 이 프로그램을 만들면서 Java script의 Event-driven 개념에 대해서 좀 더 이해할 수 있었다.

  • Events-driven concept.
  • Command line arguments

Initiate a chatting

우선, 프로그램을 통해 채팅을 즐길 클라이언트 사이드 부터 먼저 만들어 보겠다. client.js라는 파일을 만들고 다음과 같은 명령어를 사용하면 실행되도록 하겠다.

$ node client.js kimsup10

여기서 kimsup10은 프로그램 실행시 프로그램에 넘겨주는 Command line arguments로 사용자가 채팅에서 사용할 아이디를 나타낸다. 이렇게 받은 Command line arguments는 다음과 같이 처리할 수 있다.

var args = process.argv.slice(2);
if (args[0]===undefined){
    console.log("help: node client.js [id]");
    process.exit();

모든 Javascript 프로그램이 실행될 때, 프로그램에 Command line arguments를 배열의 형태로 넘긴다. 그리고 이 arguments는 process.argv에 저장되어 접근된다. 이 때 항상 0번째 인덱스의 값은 node.js가 설치된 경로, 1번째 인덱스의 값은 script 파일의 경로가 arguments로 넘어온다. 그리고 사용자가 사용하는 argument들은 2번째 인덱스부터 할당된다. 따라서 우리가 필요로 하는 값을 얻기 위해서는 process.argv의 2번째 인덱스부터 사용하면 되기에 .slice()를 이용해서 값들을 떼온다. 이 때 사용자가 아이디를 입력하지 않았으면 채팅을 시작할 수 없으므로, .slice(2)의 결과가 0일 경우에 올바른 실행 명령을 알려주고 프로세스를 종료시킨다.

Client

우리가 TCP/IP 프로토콜 상에서 통신하기 위한 모듈은 net 이다.

var net = require('net');

net.connect(options, callback) 을 이용하면, options에서 지정한 host의 port로 TCP connection을 만들 수 있다. 그리고 이 때 호출되는 콜백함수에 TCP 연결과 함께 해야할 일들을 정한다. 우선 서버에 연결됨과 동시에 사용자가 선택한 아이디를 서버로 전송한다. 그리고 채팅 프로그램에서는 서버와의 커넥션이 만들어진 후부터 사용자가 입력하는 텍스트들을 서버로 전송해야 한다. 유저가 텍스트 입력을 하는 경우를 하나의 이벤트로 만들어 놓고, 이벤트 핸들러가 서버로 텍스트를 전송하게 하면 된다. 단 /exit가 입력되는 경우는 프로세스를 종료하기로 한다.

var client = net.connect({port:8000, host:'localhost'}, function () {
    client.write(id);
    console.log('Server address : %s\nPort number : %d', address, port);
    process.stdin.on('data', function(chunk){
        str = chunk.toString();
        if (str == '/exit\n'){
            client.end();
        }
        else{
            client.write(str);
        }
    })
});

다른 유저가 입력한 텍스트를 서버를 통해 받게 되면 이 또한 event처리를 하여 사용자의 콘솔에 띄워줘야 한다. 서버 측에서 메세지를 보면 ‘data’ 이벤트가 발생하도록 되어있다.(https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback). 우리는 .on(‘data’, callback)을 통해 server측에서 data 이벤트를 발생시켰을 때 수행할 일을 정해주면 된다. 여기선 server 측이 보내주는 데이터를 출력만 해주면 된다. 마지막으로 사용자가 /exit를 입력했을 때, client 사이드에서 .end()함수를 호출하게 되어 있는데 이는 서버측과 클라이언트 측 모두에게 이벤트를 발생시키고, 클라이언트 측에서는 ‘close’이벤트가 발생한다. 이때 적절한 메세지와 함께 process를 종료 시킨다.

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

client.on('close', function () {
    console.log('Socket connection is disconnected');
    process.exit();
});

Server

var net = require('net');
var clients = [];

net.createServer(callback) 을 호출하면 서버 객체를 반환하며 서버에 클라이언트가 연결될 때마다 호출되는 ‘connection’ 이벤트에 대한 listner를 콜백 함수로 만든다. 클라이언트가 서버에 연결할때마다 콜백함수가 실행되는 것이다! 이 때 callback 함수의 인자로 만들어진 connection이 전달되며, 이 connection에 리스너를 붙임으로써 connection의 어떤 행동에 의한 이벤트 발생에 따른 다양한 일들을 할 수 있다. 우리는 이 connection들을 모아놓고 한 connenction에서 메세지를 보내 ‘data’ 이벤트를 발생시켰을 때 다른 connection들로 메세지를 보내주는 일을 하면 된다.

var server = net.createServer(function (connection) {
    clients.push(connection);
    //callback function of the below is a listner for 'connection event!'
    connection.on('data', function (data) {
        if (connection.name === undefined){
            connection.name = data.toString();
            connection.write("Welcome, "+ connection.name+ "!");
        } else {
            console.log('broadcast...>>[%s] %s', connection.name, data.toString());
            for (var i = 0; i < clients.length; i++) {
                // Error handling for undefined element, but It can make overhead from array memory.
                clients[i].write('[' + connection.name + '] ' + data.toString())
            }
        }
    });

클라이언트가 /exit을 입력하면 .end()를 호출해 서버측과 클라이언트측 모두에게 이벤트를 발생시킨다고 했다. 이 때 클라이언트 측에는 ‘close’이벤트가 발생 했는데, 서버 측에서는 ‘end’ 이벤트가 발생한다. 이 이벤트가 발생했을 때 해당 connenction을 connecton들의 모임(clients)에서 빼주어야  남아 있는 connection들에게 정상적으로 메세지를 보낼 수 있다. 이런 기능을 ‘close’이벤트의 이벤트 핸들러에 작성한다.

connection.on('end', function () {
        for (var i=0; i<clients.length; i++) {
            var deletedIndex;
            if (clients[i]===connection){
                deletedIndex = i;
            }
            else {
                console.log('broadcast...>>[%s] is disconnected', connection.name);
                clients[i].write('[from server] ' + connection.name + ' disconnected.');
            }
        }
        clients.splice(deletedIndex, 1);

    });
});

이런 기능을 하는 서버를 만들었으면, 서버가 일을하게 하면서 connection 요청이나 connection들로 부터의 이벤트를 기다리면 된다.

server.listen(8000);
console.log('TCP chat server is started!');

끝!

Issues : 아이디 중복 검사, 방 만들기 기능과 온라인 채팅.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

search previous next tag category expand menu location phone mail time cart zoom edit close