목차

node.js 에코 서버와 클라이언트 제작해보기

node.js 기반의 에코 서버, 파이썬 기반으로 대응되는 간단한 콘솔 클라이언트를 제작해봅니다.

시놉시스

서버

  1. 클라이언트가 접속하면 접속하였다는 메시지를 출력합니다.
  2. 클라이언트로부터 메시지를 받으면 받은 메시지를 그대로 출력하고, 클라이언트로 되돌려 보냅니다.
  3. 클라이언트로부터 접속이 종료되면 종료 메시지를 출력합니다.

클라이언트

  1. 서버에 접속하면 접속했다는 메시지를 출력합니다.
  2. 서버에 메시지를 보냅니다. 받은 메시지는 서버에 전달되었다가, 그대로 서버로부터 전달받습니다.
  3. 클라이언트는 간단하게 C, 파이썬으로 구현해봅니다.

서버측 구현

echo_server.js
// 필요한 객체를 선언합니다.
var net  = require('net');
var port = 9000;
 
// 소켓에 필요한 핸들러는 이 곳에 미리 선언합니다.
function server_handler(socket)
{
    socket.on('connect', onSocketConnect);
    //socket.on('data',    onSocketData);
    socket.on('end',     onSocketEnd);
    socket.on('timeout', onSocketTimeOut);
    socket.on('drain',   onSocketDrain);
    socket.on('error',   onSocketError);
    socket.on('close',   onSocketClose);
 
    // socket에 효과적으로 접근하기 위해서는 이 방법이 효과적입니다.
    socket.on('data', function (buffer) {
        onSocketData(socket, buffer)
    });
}
 
// net.Socket event handlers
function onSocketConnect()
{
    console.log('onSocketConnect: connection established');
}
 
function onSocketData(socket, buffer)
{
    var msg = 'ECHO: ' + buffer;
 
    console.log('onSocketData: data received');
    process.stdout.write(msg);
 
    if(socket.write(msg))
        console.log('onSocketData: data echoed successfully');
    else
        console.log('onSocketData: some error occured :(');
}
 
// emitted when the other side of the socket sends a FIN packet
function onSocketEnd()
{
    console.log('onSocketEnd: a client sent a FIN packet.');
}
 
function onSocketTimeOut()
{
    console.log('onSocketTimeOut: called.');
}
 
// when the write buffer becomes empty
function onSocketDrain()
{
    console.log('onSocketDrain: called.');
}
 
function onSocketError(err)
{
    console.log('onSocketError: called.');
}
 
function onSocketClose(had_error)
{
    console.log('onSocketClose: connection closed.');
 
    if(had_error)
    {
        console.log('onSocketClose: some error happend :(');
    }
    else
    {
        console.log('onSocketClose: no error :)');
    }
}
 
 
// net.Server event handler
function onServerListening()
{
    console.log('onServerListening: listening on port ' + port);
    console.log('Press \'x\' to exit...');
}
 
function onServerConnection(socket)
{
    var ip   = socket.remoteAddress;
    var port = socket.remotePort;
 
    console.log('onServerConnection: connection received from '+ip+':'+port);    
}
 
function onServerClose()
{
    console.log('onServerClose: server closed');
    process.exit(0);
}
 
function onServerError(err)
{
     console.log('onServerError: called');
}
 
function onStdinData(server, data)
{
    console.log('onStdinData: '+data);
    console.log('onStdinData: length '+data.length);
    if(data == 'x\n')
    {
        console.log('onStdinData: termination command received');
        server.close();
        process.exit(0);
    }
}
 
function onStdinEnd()
{
    console.log('onStdinEnd: called');
}
 
 
////////////////////////////////////////////////////////////////////////////////
// Beginning
// argument parsing
if (process.argv.length != 3)
{
    console.log('Usage: node echo_server.js <port>');
    process.exit(0);    
}
else
{
    port = process.argv[2]
}
 
// 서버 생성
var server = net.createServer(server_handler);
// 서버의 이벤트를 설정합니다.
// server.on('data', );
server.on('listening',  onServerListening);
server.on('connection', onServerConnection);
server.on('close',      onServerClose);
server.on('error',      onServerError);
// 서버는 대기
server.listen(port);
// stdin 활성화
process.stdin.resume();
process.stdin.on('data', function(data){onStdinData(server, data);});
process.stdin.on('end', onStdinEnd);

사용법

node echo_server.js 9000

클라이언트 측 스크립트

echo_client.js
// 필요한 객체를 선언합니다.
var net  = require('net');
 
// net.Socket event handlers
function onSocketConnect()
{
    console.log('onSocketConnect: connection established');
}
 
function onSocketData(socket, buffer)
{
    console.log('onSocketData: data received');
    process.stdout.write(buffer);
}
 
// emitted when the other side of the socket sends a FIN packet
function onSocketEnd()
{
    console.log('onSocketEnd: a client sent a FIN packet.');
}
 
function onSocketTimeOut()
{
    console.log('onSocketTimeOut: called.');
}
 
// when the write buffer becomes empty
function onSocketDrain()
{
    console.log('onSocketDrain: called.');
}
 
function onSocketError(err)
{
    console.log('onSocketError: called.');
}
 
function onSocketClose(had_error)
{
    console.log('onSocketClose: connection closed.');
 
    if(had_error)
    {
        console.log('onSocketClose: some error happend :(');
    }
    else
    {
        console.log('onSocketClose: no error :)');
    }
 
    process.exit(0);
}
 
function onStdinData(socket, data)
{
    socket.write(data);
    console.log('onStdinData: data sent');
}
 
 
////////////////////////////////////////////////////////////////////////////////
// Beginning
// argument parsing
if (process.argv.length != 4)
{
    console.log('Usage: node echo_client.js <host> <port>');
    process.exit(0);    
}
 
var host = process.argv[2];
var port = process.argv[3];
 
// 소켓 생성
var options = {'port': port, 'host': host};
var socket  = net.createConnection(options);
 
// 핸들러 설정
socket.on('connect', onSocketConnect);
socket.on('end',     onSocketEnd);
socket.on('timeout', onSocketTimeOut);
socket.on('drain',   onSocketDrain);
socket.on('error',   onSocketError);
socket.on('close',   onSocketClose);
 
// socket에 효과적으로 접근하기 위해서는 이 방법이 효과적입니다.
socket.on('data', function (buffer) {
    onSocketData(socket, buffer)
});
 
// stdin 활성화
process.stdin.resume();
process.stdin.on('data', function(data){onStdinData(socket, data);});

사용법

node echo_client.js 127.0.0.1 9000