io (1)

💻 Programming

[ Node.js in Action ] 13장 요약

/**
 *  웹서버 그 이상의 것들
 * - Socket IO
 * - TCP/IP 네트워킹
 * - OS와 상호작용하기 위한 툴
 * - 커맨드라인 툴 개발하기
 */


13.1. Socket.io


Socket IO 는 WebSocket과 유사한 기능을 제공하면서도, broadcasting, volatile 메시징을 편리하게 구현할 수 있는 API를 제공합니다.

WebSocket은 WebSocket 프로토콜을 사용하는데 아직 완성된 프로토콜이 아니라서 브라우저 마다 지원여부에도 차이가 있다는 문제가 있습니다. Socket IO를 이용할 경우 이 문제를 해결할 수 있는데, Socket IO는 WebSocket을 지원하지 않는 브라우저의 경우에 각 브라우저별로 다양한 방법을 통해 내부적으로 메시지를 보낼 수 있다고 합니다.


 A minimal Socket.IO application that pushes the server’s time to connected clients
 A Socket.IO application that triggers page refreshes when CSS files are edited


13.1.1 초간단 Socket.IO 애플리케이션 만들기


<< 간단한 Socket IO clock-server 프로그램 >> ( 시간이 표시가 안되는데 이유를 모르겠음 ;;;; )


13.1.2 Socket.IO를 이용한 페이지와 CSS reloading 하기


웹페이지 디자인을 할 때 일반적인 순서

1 Open the web page in multiple browsers.
2 Look for styling on the page that needs adjusting.
3 Make changes to one or more stylesheets.
4 Manually reload all the web browsers.
5 Go back to step 2.


<< 위 4번 과정을 쉽게 만들어주는 프로그램 예제 >> ( 이놈도 제대로 동작을 하지 않음...;;;)


13.1.3 Socket.IO의 또 다른 사용법


Socket.IO는 프로그레스 바를 계속 업데이트해서 수치가 올라가는 것 보여주는 기능에 적합함




13.2 TCP/IP 네트워킹


노드는 네트워킹 애플리케이션에 매우 적합. 네트워킹은 많은 I/O를 요구하기 때문.


 Working with buffers and binary data
 Creating a TCP server
 Creating a TCP client


13.2.1 Buffers와 Binary Data 다루기


Buffer 데이타 타입은 노드에서 제공하는 특별한 데이타 타입이다.

고정 길이의 raw binary data처럼 동작하며, C에서의 malloc()이나 C++, JAVA에서의 new 키워드라고 생각해도 된다.

기본적으로 Stream 클래스에 의해 데이타 이벤트 내에 반환된다.

Buffer는 숫자 0~ 255까지만 저장할 수 있는 non-resizable 배열이다.


TEXT DATA VS. BINARY DATA

1. -----------------------------------

var b = new Buffer("121234869");
console.log(b.length);
9
console.log(b);
<Buffer 31 32 31 32 33 34 38 36 39>


2. -----------------------------------

var b = new Buffer(4);
b.writeInt32LE(121234869, 0);
console.log(b.length);
4
console.log(b);
<Buffer b5 e5 39 07>

-----------------------------------


13.2.2 TCP 서버 만들기 


Socket 클래스는 client와 server 양쪽에서 모두 사용됨

It’s a Stream subclass that’s both readable and writable (bidirectional).

That is, it emits data events when input data has been read from the socket,

and it has write() and end() functions for sending output data.


WRITING DATA


var net = require('net');
net.createServer(function (socket) {
socket.write('Hello World!\r\n');
socket.end();
}).listen(1337);
console.log('listening on port 1337');


$ telnet localhost 1337


READING DATA


socket.on('data', function (data) {
console.log('got "data"', data);
});


data 인자는 기본적으로 Buffer인스턴스이다.

setEncoding()을 이용해서 decoded String을 인자로 넘길 수도 있다.


socket.on('end', function () {
console.log('socket has ended');
});


end 이벤트를 리스닝함으로써 불필요한 데이터 전송을 하지 않을 수 있다.


SAMPLE TEST  


var net = require('net');
var socket = net.connect({ host: process.argv[2], port: 22 });
socket.setEncoding('utf8');
socket.once('data', function (chunk) {
console.log('SSH server version: %j', chunk.trim());
socket.end();
});


$ node client.js github.com


SOCKET.PIPE() 을 사용하여 두 개의 스트림 연결하기


The pipe() 함수는 readable 스트림에서 데이터를 읽어와서 쓰기가능한 스트림에 데이터를 쓰는 역할을 합니다.


비정상 연결끊김 핸들링하기

클라이언트가 netcat을 썼을 때 Ctrl-D 대신 Ctrl-C로 커넥션을 종료시키면 서버쪽 소켓이 깔끔하게 종료가 되지 않는데, 이 부분에 대한 뒷처리를 하려면 아래처럼 close 이벤트를 리스닝하면 된다.


socket.on('close', function () {
console.log('client disconnected');
});


FINAL TEST APP


server


var net = require('net');
net.createServer(function (socket) {
console.log('socket connected!');
socket.on('data', function (data) {
console.log('"data" event', data);
});
socket.on('end', function () {
console.log('"end" event');
});
socket.on('close', function () {
console.log('"close" event');
});
socket.on('error', function (e) {
console.log('"error" event', e);
});
socket.pipe(socket);
}).listen(1337);


client


var net = require('net');
var host = process.argv[2];
var port = Number(process.argv[3]);
var socket = net.connect(port, host);
socket.on('connect', function () {
process.stdin.pipe(socket);
socket.pipe(process.stdout);
process.stdin.resume();
});
socket.on('end', function () {
process.stdin.pause();
});



13.3 Tools for interacting with the operating system


 The global process object—Contains information about the current process, such
as the arguments given to it and the environment variables that are currently set
 The fs module—Contains the high-level ReadStream and WriteStream classes
that you’re familiar with by now, but also houses low-level functions that we’ll
look at
 The child_process module—Contains both low-level and high-level interfaces
for spawning child processes, as well as a special way to spawn node instances
with a two-way message-passing channel


13.3.1 The process global singleton


Every Node process has a single global process object that every module shares access
to. Useful information about the process and the context it’s running in can be found
in this object.


the most interesting feature of the process object is that it’s an EventEmitter instance, which emits very special events,
such as exit and uncaughtException.


USING PROCESS.ENV TO GET AND SET ENVIRONMENT VARIABLES


SPECIAL EVENTS EMITTED BY PROCESS


 exit gets emitted right before the process exits.


process.on('exit', function (code) {
console.log('Exiting...');
});


 uncaughtException gets emitted any time an unhandled error is thrown.


process.on('uncaughtException', function (err) {
console.error('got uncaught exception:', err.message);
process.exit(1);
});
throw new Error('an uncaught exception');


CATCHING SIGNALS SENT TO THE PROCESS 


three signals that Node handles by default


 SIGINT—Sent by your shell when you press Ctrl-C. Node’s default behavior is to
kill the process, but this can be overridden with a single listener for SIGINT on
process.
 SIGUSR1—When this signal is received, Node will enter its built-in debugger.
 SIGWINCH—Sent by your shell when the terminal is resized. Node resets
process.stdout.rows and process.stdout.columns and emits a resize event
when this is received.


13.3.2 Using the filesystem module


MOVING A FILE 


fs.rename()

원격으로 이동시키려면 stream을 이용해서 복사해야 함. rename은 원격지로 이동하는 것을 지원하지 않음.


WATCHING A DIRECTORY OR FILE FOR CHANGES 


fs.watch() - 플랫폼의 네이티브 파일 변경 알림 API를 이용함. (플랫폼 차이로 인해 unreliable 함)

fs.watchFile()


USING COMMUNITY MODULES: FSTREAM AND FILED 


fstream을 이용하면 cp -rp srcDir destDir 명령어를 쉽게 구현할 수 있다.


filed 인스턴스는 req, res 객체에 접근이 가능하다.

http.createServer(function (req, res) {
req.pipe(filed('path/to/static/files')).pipe(res);
});

위 코드는 파일이 캐쉬되었을 때 304 Not Modified 오류를 뱉어내고 파일을 디스크에서 열거나 읽는 행위는 하지 않는다.



13.3.3 Spawning external processes


 cp.exec()—A high-level API for spawning commands and buffering the result
in a callback
 cp.spawn()—A low-level API for spawning single commands into a Child-
Process object
 cp.fork()—A special way to spawn additional Node processes with a built-in
IPC channel


자식 프로세스의 장단점 (PROS AND CONS TO CHILD PROCESSES)

 

cons : child process 로 실행되는 애플리케이션이 이미 클라이언트에 설치가 되어있어야 한다.

pros : 다른 언어로 쓰여진 애플리케잇션을 이용해서 좀 더 rich한 애플리케이션 개발이 가능하다.


cp.exec()로 명령어 결과 버퍼링하기(BUFFERING COMMAND RESULTS USING CP.EXEC())


cp.exec(), is useful for when you want to invoke a command, and you only care about the final result. This API allows you to enter full sequences of commands, including multiple processes piped to one another.


SPAWNING COMMANDS WITH A STREAM INTERFACE USING CP.SPAWN()


cp.spawn() returns a ChildProcess object that you can interact with.


ex)

var child = cp.spawn('ls', [ '-l' ]);
// stdout is a regular Stream instance, which emits 'data',
// 'end', etc.
child.stdout.pipe(fs.createWriteStream('ls-result.txt'));
child.on('exit', function (code, signal) {
// emitted when the child process exits
});


DISTRIBUTING THE WORKLOAD USING CP.FORK()


Like cp.spawn(), cp.fork() returns a ChildProcess object.

The major difference is the API added by the IPC channel: the child process now has a child.send (message) function, and the script being invoked by fork() can listen for process.on('message') events.


피보나치 수열 계산 예제


13.4 커맨드라인 툴 개발하기(Developing command-line tools )


 Parsing command-line arguments
 Working with stdin and stdout streams
 Adding pretty colors to the output using ansi.js


13.4.1 Parsing command-line arguments


Node provides you with the process.argv property, which is an array of strings, which are the arguments that were used when Node was invoked.

The first entry of the array is the Node executable, and the second entry is the name of your script.


ex)

var args = process.argv.slice(2);
console.log(args);


$ node args.js
[]


$ node args.js hello world
[ 'hello', 'world' ]


$ node args.js "tobi is a ferret"
[ 'tobi is a ferret' ]


어떤 옵션이 들어왔느냐에 따라 usage프린트하는 예제


13.4.2 stdin, stdout 가지고 놀기 (Working with stdin and stdout)


 process.stdin—A ReadStream to read input data from
 process.stdout—A WriteStream to write output data to


WRITING OUTPUT DATA WITH PROCESS.STDOUT


console.log() 내에서 process.stdout.write()이 사용됨.


(이클립스에서 argument 설정해주고 실행한다고 가정)

var target = process.argv[2];
process.stdout.write('Entered value is : ' + target);


PROCESS.STDIN 로 입력값 읽어오기 (READING INPUT DATA WITH PROCESS.STDIN )

 

Before you can read from stdin, you must call process.stdin.resume() to indicate that your script is interested in data from stdin.

After that, stdin acts like any other readable stream, emitting data events as data is received from the output of another process, or as the user enters keystrokes into the terminal window.


나이 검증하는 예제


DIAGNOSTIC LOGGING WITH PROCESS.STDERR 


 process.stderr을 직접 사용하지말고 console.error()를 사용하자.


13.4.3 출력에 색 입히기 (Adding colored output)


ANSI 이스케이프 코드 작성하기 (CREATING AND WRITING ANSI ESCAPE CODES )


(cmd 창에서 ansi.js 실행)


console.log('\033[43m\033[35mhello\033[39m\033[49m');

var ansi = require('ansi');
var cursor = ansi(process.stdout);

cursor
.fg.green()
.write('This should be green.')
.fg.reset()
.write('\n');