LearnNode 第三章 部分翻譯 附 原文

第三章   Node核心代碼

 

第一章用傳統的例子打印hello的node應用讓我門認識了node,本章中用幾個模塊實例給大家講解了什麼事node核心代碼。這些api提供了很多必須的函數幫助我們創建應用

 

在本章中,我將會提供一些詳細的例子講述node核心系統,他不是一個徹底的概述,這些api非常多而且不斷的改動,所以我只是抓住重點的api,並且在以後的章節中能用到的,或者足夠複雜值得我們學習的

 

全局的:Global, Process, Buffer

有一些對象可用在所有的node應用而且不需要納入任何的模塊,node.Js站點把他們表示在標籤globals下。

我們曾經使用一個全局的,require,來包含我們在應用中所需要的模塊,我們也使用了大量使用過其他全局,console,來記錄信息到控制檯,他們對於node的底層實現是必不可少的。但是卻不一定我們所有的都必須知道的很清楚,其中一些非常的重要需要仔細看,因爲他們定義了關鍵的node如何工作的方面

 

尤其,我們將仔細的看下面的內容

.Global對象:全局的命名空間

Process:提供必要的函數,比如封裝了三個輸入輸出流,並且用函數轉化非阻塞的到阻塞的回調

Buffer:提供未處理的數據存儲和操縱

 

Global:

Global是全局的命名空間對象,在一些方式中,他類似於windows在瀏覽器環境中,在這裏,他提供獲取全局變量和方法的入口並且不必通過名字聲明引用

 

從REPL中,你可以打印global對象到控制檯

> console.log(global)

打印出的是其他所有全局對象的藉口,以及你在系統運行的信息

 

我曾經提到Global類似於windows在瀏覽器中的對象,但是他們有幾個關鍵區別,不僅僅是方法和屬性變量,win在瀏覽其中的對象是全局的,如果你在javascript客戶端中定義一個全局變量,他被網頁接收,和每一個簡單的庫,然而,如果你在node模塊中創建一個變量,它只是模塊中的全局變量,並不是所有的模塊,就像我們在example1-3和example1-4的例子

 

當你定義一個模塊全局的變量時,你可以觀察全局變量發生什麼,,第一:定義一個頂層的變量

Ø       var test = "This really isn't global, as we knowglobal";

Ø      然後打印global

Ø      Console.log(global);

你將會看到你的變量,作爲global的一個新的變量成員,在最下面,。對於另一個有趣的屬性,把glbal’賦值給另一個變量,但是不用var關鍵字

gl =global;

這個global對象接口將會打印在控制檯上,而且在最下面,你將會看到本地變量標記爲引用

 

任何其他的全局對象或者方法,包括require,是global對象口得一部分

 

當node開發者討論context,他們經常是指global對象,在例子2-1中,這些代碼接收context對象當他們創建REPl對象,context對象是一個global對象,當一個應用創建了REPL客戶端,他存在了一個新的context,在這種情況下,擁有自己獨特的global對象。重載他們和用存在global對象的方法是去定製一個REPL並且設置useGlobal標誌爲true,而不是默認的false

模塊存在於他們獨特的全局命名空間中,意味着如果你在一個模塊中定義一個頂層的變量,他並不是其他模塊的變量。更重要的是,他意味着,只有明確從其他模塊引用才能成爲這個應用的一部分,關鍵點:你不能從一個應用或其他模塊中獲取一個頂層的變量。

 

證明,接下來的代碼包含了一些簡單的模塊擁有一個頂層變量 globalValue,而且函數設置並返回這個變量,在函數中,返回這個值,這個全局得對象用console.log方法打印

 

var globalValue;

exports.setGlobal= function(val) {

globalValue =val;

};

exports.returnGlobal= function() {

console.log(global);

returnglobalValue;

};

我們可能期待打印globalobject時可以看到globalValue,然而當我們設置變量時,他並沒有出現

啓動一個REPL會話,並申請require包含這個模塊

 

Ø       var mod1 = require('/mod1.js');

Ø      設置變量的值然後獲取變量

> mod1.setGlobal(34);

> var val = mod1.returnGlobal();

Console.log方法在返回golble定義變量之前打印global對象,我們可以在最下面看到他,這個變量持有一個引用模塊的句柄,而且新的變量標記了返回的值,但是沒有對頂層變量globalValue的引

 

mod1: { setGlobal:[Function], returnGlobal: [Function] },

_: undefined,

val: 34 }

當輸出的模塊打印了全局對象,我們也看不到模塊自由的變量,唯一我們所有的入口,是模塊提供的。對於javascript開發者來說,這意味着沒有更多的期待和複雜的名字衝突

Process:

每一個Node應用都是一個Node進程對象的實例,並且,正因如此。帶有很多內置的功能

 

許多的進程對象的方法和屬性提供了關於應用和環境的識別信息,process.execPath方法返回一個確定node應用的路徑,process.vesion,提供node的版本,而process.platform識別服平臺

 

console.log(process.execPath);

console.log(process.version);

console.log(process.platform);

 

上面代碼返回一下信息

路徑

Node版本

操作系統

 

Process對象當然也包括stdio流,stdin,stdout和stderr,stdin和stdout是異步的,並且是可讀寫的,而stderr是同步阻塞的流

 

證明怎樣讀取和寫入信息,在例子3-1,這個node程序監聽stdin信息,並且重複數據到stdout。標準輸入流默認情況下是暫停的,所以我們需要在發送數據錢需要發送resume

 

Example3-1分別從stdin和stdout寫入和讀取數據

 

Process.stdin.resume();

 

Process.stdin.on(‘data’,function(chunk) {

       Process.stdout.write(‘data:’ + chunk);

});

運行這個程序,然後,在控制檯啓動,每次你輸入一些東西然後回車,那麼將會返回你發鬆的信息

另一個有用的Process方法是memoryUsage,可以告訴我們node程序用了多少的內存,這對於我們進行性能調節有很大的用處嗎,並且對我們的應用有一些好奇,這個函數的會用是下列的結構

{ rss: 7450624, heapTotal: 2783520,heapUsed: 1375720 }

 

HeapTotal和headUsed屬性參閱V8引擎的內存使用

 

最後一個process方法,我準備介紹peocess.nextTick,這個方法連了一個啓動事件循環下一事件的回調函數

 

你用process.nextTick的原因是因爲你因某些原因延遲一個函數,但是你想異步的延遲他,一個好的例子如果你想創建一個擁有回調函數做參數新的函數並且你想保證那個回調函數是不同步的,下面的代碼是證明過的

FunctionasyncFunction = function (data, callback) {

       Process.nextTick(function() {

       Callback (val);

});

}

 

如果僅僅是調用回調函數,那麼這個動作是同步的,現在,這個回調函數將不會調用直到下個事件循環,而不是現在

 

你可以用setTimeout替代process.nextTick

setTimeout(function(){

       callback(vall);

}, 0);

 

然而,setTimeout並不像process.nextTick那樣有效率,當一起測試的時候,pocess.NextTick調用速度明顯比setTimeout要快

另一個我們使用process.nextTick的原因是如果你運行一個應用有一個運算量很複雜的函數,時間消耗和操作。你可以打破這個過程,每次調用process.nextTick,來接收其他的node請求而不用等待這個進程的結束

 

當然,這個反過程你不想打破這個順序,你需要保證這個進程按順序執行,因爲你可能得到不想要的結果

 

Buffer

Buffer這個類是在node用二進制數據的句柄,在流中,服務,和套接字內容在後面的章節中,我們將要討論這樣一個事實我們經常使用二進制數據而不是字符串。把二進制數據轉換爲字符串,這個對於流數據編碼用setEncoding來設置,從字符創建一個流,我們大多數情況會創建一個新的流

Var vuf =new Buffer(string);

如果你從字符串創建了一個流,你可以傳遞第二個參數來設置編碼,大概的代碼如下

Ascii – 7字節的ASCII

Utf8-多字節的unicode編碼

Usc2 – 兩個字節的unicode編碼

Base64- 基礎的64字節編碼

Hex:每個字節是兩個十六位進制的字符

我們也可以把一個字符寫入一個已存在的流中,提供一個確定的便宜,寬度和編碼

Buf.write(string);//偏移默認爲0, 寬度默認爲buffer的寬度 – offset,編碼爲utf8

 

數據在socket之間傳輸是默認要轉換爲二進制數據的,爲了發送一個字符串,你需要在socket中明確的使用setEncoding,或者你可以在socket的函數中寫明編碼方式,默認的,TCP socket.write方法設置第二個參數爲“utf8”,但是socket返回connectionListener回調TcPcreateServer發送流數據而不是字符串

 

這裏有很多的方法來讀取和寫各種各樣的數據流,比如buffer.readInt8和buffer.writeUInt8

定時器: setTimerout,clearTimeout,setInterbal, clearInterval

在javascript客戶端的定時器是全局的,他們並不是javascript的一部分,但是變成了javascript開發者把他們加入nodeapi核心的必不可少的部分

 

在node中的計時器函數操作就像在瀏覽器中的,事實上,他們的運行方式和在谷歌瀏覽器中是一樣的,node是基於谷歌V8javascript引擎的

Node的setTimeout函數用回調函數作爲第一個參數,延遲的時間作爲第二個參數,還有一個可選的參數列表:

//time toopen file and read content to http response object

Functionon_OpenAndReadFile(filename, res) {

       Console.log(‘opening’ + filename);

 

//open and read in file contents

       Fs.readFile(filename, ‘utf8’,function(err, data) {

       If(err)

              Res.write(‘Couldnot find or open file for reading\n’);

       Else{

              Res.write(data);

}    

}

//response is done

Res.end();

}

 

setTimeout(openAndReadFile,2000, filename, res);

在這段代碼中,2000ms之後回調函數on_OpenAndReadFile打開並且讀取文件並把內容通過http相應給客戶端

就像node文檔筆記,這裏並沒有保證回調函數將會在多少秒後執行,這裏和調用在瀏覽器setTimeout並沒有區別,我們對環境沒有絕對的控制權,

 

函數clearTimeout清空setTimeout的設置,如果你需要一個重複地計時器,你可以用setInterval來運行一個函數每n ms,n是第二個參數傳遞給函數,清空這個間隔請使用clearInterval

 

Servers,Steams,Sockets

大多數的node核心api必須創建一個服務來監聽特定的對話,在第一張中,我們使用了http模塊創建了http網絡服務,其他的方法創建了TCP服務,一個TLS服務,和一個UDP套接字。接下來我將會介紹TLS,在第十三章中,涵蓋的完全,但是在本章中,我想要介紹tcp和udpnode核心函數。首先,簡要介紹下本章中的術語

 

一個socket的節點在通信端,而另一個network skocket的節點在另一個不同電腦上運行的網絡應用,數據傳送是以流的形式。數據可以以二進制的形式傳輸,在一個緩衝區中,在unicode中或者字符串中。所有形式的數據都是以包的形式傳輸。部分數據被分在不同的快中。這裏有一個特殊的包,fin,或者finish包,這個包是socket發送標誌着數據發送結束了。這個會話是怎樣管理的,怎樣意識到流,是socket創建時應該考慮的

 

TCPSockets和Servers

我們可以用Net模塊創建一個基礎的TCP服務端和客戶端,Tcp 對於大部分的網絡程序是基礎形式,比如web服務和email,他提供了一種在服務器和客戶端傳遞數據的形式

 

創建一個TCP服務和創建http服務有一些不同,我們創建服務,傳遞一個回調函數。TCp服務和http服務不同,而是傳遞一個requestListener,TCP回調函數的唯一參數是一個socket的實例,監聽進入的連接

 

Example3-2 一個創建TCP服務的例子,有一個監聽8124會話端口的監聽器

Var net =require(‘net’);

 

Varserver = net.createServer(function (conn) {

                     Console.log(‘connected’);

 

                     Conn.on(‘data’,function(data) {

       Console.log(data+ ‘from’ + conn.remoteAddress + ‘ ’ +

              Conn.remotePort);

       Conn.write(‘Repeating:’ + data);

});

Conn.on(‘close’, function() {

       Console.log(‘client closed connection’);

});

 

}).listen(8124);

 

Console.log(‘listening on port8124);’

對於createServer:allowHalfOpen有一個任選參數,設置這個參數爲true,當從客戶端接收到結束消息時不發送fin,這個操作保持socket打開狀態,關閉這個socket,你可以需要明確的用end方法,默認的allowHalfOpen是false

注意一個通過on方法回調函數附加到一個兩個事件監聽的函數的方法,在Node中,許多的對象發送事件通過對方法的使用提供一個把一個函數作爲事件監聽的方法。這個方法把第一個事件名作爲第一個參數,而事件監聽作爲第二個參數

 

TCP客戶端僅僅是簡單的創建一個服務,就像3-3顯示的一樣,在客戶端調用的setEncoding方法改變了接收信息的編碼方式,就像前幾個章節討論的一樣,數據傳輸用的是非人類可讀的緩衝技術,但是我們可以用setEncoding改成字符串來閱讀他們。Socket的write方法經常用來傳輸數據,他也對兩個事件附加了監聽器函數嗎,數據,對於接收到的數據close,讓server關閉連接

 

Example3-3 客戶端socket發送數據給Tcp服務端

Var net =require(‘net’);

Varclient = new net.Socket();

Client.setEncoding(‘utf8’);

 

//connectto server

Client.connect(‘8124’,‘localhost’, function () {

       Console.log(‘connected to server’);

Client.write(‘whoneeds a broser to communicate?’);

});

 

//preaparefor input from terminal

Process.stdin.resume();

 

//whenreceive data, send to server

Process.stdin.on(‘data’,function(data) {

       Client.write(data);

});

 

//whenreceive data back, print to console

Client.on(‘data’,function(data) {

       Console.log(data);

});

 

//whenserver closed

Client.on(‘close’,function() {

       Console.log(‘connection is closed’);

});

 

數據在兩個套接字的終端之間進行傳輸,,當你按下回車時進行傳輸,客戶端的應用首先發送你輸入的字符串,TCP服務寫到控制檯的。服務端重複信息發回到客戶端,服務器端也打印IP地址和客戶端的端口通過socket的remoteAddress和remotePort的屬性,下面是服務器端的控制檯輸出

客戶端和服務器端之間的連接直到CTRL +C纔會停止,無論哪個socket在接收close事件之前都是開着的。服務器端也可以保持多個客戶端,所有相關的函數都是不同步的

就像我前面提及的,TCP有很多的根本的傳輸原理,包括http

 


3
The Node Core
Chapter 1 provided a first look at a Node application with the traditional (and always entertaining) Hello,
World application. The examples in the chapter made use of a couple of modules from what is known as
the Node Core: the API providing much of the functionality necessary for building Node applications.
In this chapter, I'm going to provide more detail on the Node core system. It's not an exhaustive overview,
since the API is quite large and dynamic in nature. Instead, I'm focusing on key elements of the API, and
taking a closer look at those that we'll use in later chapters, and/or are complex enough to need a more indepth
review.
Node.js documentation for current stable release can be found at http://nodejs.org/api/.
Globals: Global, Process, and Buffer
There are several objects available to all node applications without having to incorporate any module. The
Node.js web site groups these items under the descriptive label of "Globals".
We've been using one global, require, to include modules into our applications. We've also made
extensive use of another global, console, to log messages to the console. Others are essential to the
underlying implementation of Node, but aren't necessarily anything we'd access or need to know about
directly. Some, though, are important enough taking a closer look at., because they help define key aspects
of how Node works.
In particular, we're going to take a closer look at:
• The Global object: the global namespace
• Process: provides essential functionality, such as wrappers for the three STDIO streams, and
functionality to transform a synchronous function into a asynchronous callback
• Buffer: Provides raw data storage and manipulation
Global
Global is the global namespace object. In some ways, it's similar to windows in a browser environment, in
that it provides access to global properties and methods and doesn't have to be explicitly referenced by
name.
From REPL, you can print out the global object to the console:
> console.log(global)
Download from Wow! eBook <www.wowebook.com>
What prints out is the interface for all of the other global objects, as well as a good deal of information
about the system in which you're running.
I mentioned that Global is like the windows object in a browser, but there are key differences—and not
just the methods and properties available. The windows object in a browser is truly global in nature. If
you define a global variable in client-side JavaScript, it's accessible by the web page, and every single
library. However, if you create a variable at the top level scope in a Node module (a variable outside a
function), it only becomes global to the module, not to all of the modules, as we discovered with Examples
1-3 and 1-4 in Chapter 1.
You can actually see what happens to the Global object when you define a module/global variable in
REPL. First, define the top-level variable:
> var test = "This really isn't global, as we know global";
Then print out Global:
> console.log(global);
You should see your variable, as a new property of global, at the bottom. For another interesting
perspective, assign global to a variable, but don't use the var keyword:
gl = global;
The global object interface is printed out to the console, and at the bottom you'll see the local variable
assigned as a circular reference:
> gl = global;
...
gl: [Circular],
_: [Circular] }
Any other global object or method, including require, is part of the Global object's interface.
When Node developers discuss context, they're really referring to the Global object. In Example 2-1 in
Chapter 2, the code accessed the context object when creating a custom REPL object. The context object
is a Global object. When an application creates a custom REPL, it exists within a new context, which in
this case means, has its own Global object. The way to override this and use the existing Global object is to
create a custom REPL and set the useGlobal flag to true, rather than the default false.
Modules exist in their own global namespace, which means that if you define a top-level variable in one
module it is not available in other modules. More importantly, it means that only what is explicitly
exported from the module becomes part of whatever application includes the module. Point of fact: you
can't access a top-level module variable in an application or other module, even if you deliberately tried.
To demonstrate, the following code contains a very simple module that has a top-level variable named
globalValue, and functions to set and return the value. In the function that returns the value, the Global
object is printed out using a console.log method call.
var globalValue;
exports.setGlobal = function(val) {
globalValue = val;
};
exports.returnGlobal = function() {
console.log(global);
return globalValue;
};
We might expect that in the print out of the Global object we'll see globalValue, as we do when we set
a variable in our applications. This doesn't happen, though.
Start a REPL session and issue a require call to include the new module:
> var mod1 = require('/mod1.js');
Set the value and then ask for the value back:
> mod1.setGlobal(34);
> var val = mod1.returnGlobal();
The console.log method prints out the global object before returning its globally defined value. We
can see at the bottom, the new variable holding a reference to the imported module, and the new variable
assigned the returned value...but no reference to that module's own top-level globalValue:
mod1: { setGlobal: [Function], returnGlobal: [Function] },
_: undefined,
val: 34 }
Even when the exported Module object prints out the Global object, we don't see the module's own global
data. The only access we have to it, is by whatever means the module provides. For JavaScript developers,
this means no more unexpected and harmful name collisions because of accidental or intentional global
variables in libraries.
Process
Each Node application is an instance of a Node Process object, and as such, comes with certain built-in
functionality.
Many of the Process object's methods and properties provide identification or information about the
application and its environment. The process.execPath method returns the execution path for the
Node application, process.version, provides Node version, and process.platform identifies
the server platform:
console.log(process.execPath);
console.log(process.version);
console.log(process.platform);
This code returns the following in my system (at the time when this was written):
/usr/local/bin/node
v0.6.9
linux
The Process object also wraps the STDIO (Standard IO) streams stdin, stdout, and stderr. Both stdin and
stdout are asynchronous, and are readable and writable, respectively. The stderr stream is a synchronous,
blocking stream.
To demonstrate how to read and write data from stdin and stdout, in Example 3-1 the Node application
listens for data in stdin, and repeats the data to stdout. The stdin stream is paused by default, so we have to
issue a resume call before sending data.
Example 3-1. Reading and writing data to stdin and stdout, respectively
process.stdin.resume();
process.stdin.on('data', function (chunk) {
process.stdout.write('data: ' + chunk);
});
Run the application using Node, and then, start typing into the terminal. Every time you type something
and hit ENTER, what you typed is reflected back to you.
Another useful Process method is memoryUsage, which tells us how much memory the Node application
is using. This could be helpful for performance tuning, and just general curiosity about the application. The
response has the following structure:
{ rss: 7450624, heapTotal: 2783520, heapUsed: 1375720 }
The heapTotal and heapUsed properties refer to the V8 engine's memory usage.
A last Process method I'm going to cover is process.nextTick. This method attaches a callback
function that's fired during the next tick (loop) in the Node event loop.
The reason you would use process.nextTick is if you want to delay a function for some reason, but
you want to delay it asynchronously. A good example would be if you're creating a new function that has a
callback function as parameter and you want to ensure that the callback is truly asynchronous. The
following code is a demonstration:
function asynchFunction = function (data, callback) {
process.nextTick(function() {
callback(val);
});
);
If we just called the callback function, then the action would be synchronous. Now, the callback function
won't be called until the next tick in the event loop, rather than right away.
You could use setTimeout with a zero (0) millisecond delay instead of process.nextTick:
setTimeout(function() {
callback(val);
}, 0);
However, setTimeout isn't as efficient as process.nextTick. When both were tested against each
other, process.nextTick was called far more quickly than setTimeout with a zero (0) millisecond
delay.
Another reason you could use process.nextTick is if you're running an application that has a function
performing some computationally complex, and time consuming, operation. You could break the process
into sections, each called via process.nextTick, to allow other requests to the Node application to be
processed without waiting for the time consuming process to finish.
Of course, the converse of this is you don't want to break up a process where you need to ensure that the
process is processed sequentially, because you may end up with unexpected results.
Buffer
The Buffer class is a way of handling binary data in Node. In the Streams, Servers, and Sockets section
later in the chapter, we'll cover the fact that streams are oftentimes binary data rather than strings. To
convert the binary data to a string, the data encoding for the stream socket is changed using
setEncoding. To create a buffer from a string, we typically create a new buffer:
var buf = new Buffer(strng);
If you create a buffer to hold a string, you can pass in an optional second parameter with the encoding.
Possible encodings are:
• ascii - 7 bit ASCII
• utf8 - multibyte encoded Unicode characters
• usc2 - 2 bytes, little endian encoded Unicode characters
• base64 - Base64 encoding
• hex - Encodes each byte as two hexadecimal characters
We can also write a string to an existing buffer, providing an optional offset, length, and encoding:
buf.write(strng); // offset defaults to 0, length defaults to
buffer.length - offset, encoding is utf8
Data sent between sockets is transmitted as a buffer (in binary format), by default. To send a string instead,
you either need to call setEncoding directly on the socket, or specify the encoding in the function that
writes to the socket. By default, the TCP socket.write method does set the second parameter to 'utf8'
by default, but the socket returned in the connectionListener callback to the TCP createServer
function sends the data as a buffer, not a string.
There are several methods for reading and writing various types of data to the buffer, such as buffer.
readInt8, and buffer.writeUInt8.
The Timers: setTimeout, clearTimeout, setInterval,
clearInterval
The timer functions in client-side JavaScript are part of the global windows object. They're not part of
JavaScript, but have become such an ubiquitous part of JavaScript development that the Node developers
incorporated them into the Node core API.
The timer functions operate in Node just like they operate in the browser. In fact, they operate in Node
exactly the same as they would in Chrome, since Node is based on Chrome's V8 JavaScript engine.
The Node setTimeout function takes a callback function as first parameter, the delay time (in
milliseconds) as second parameter, and an optional list of arguments:
// timer to open file and read contents to HTTP response object
function on_OpenAndReadFile(filename, res) {
console.log('opening ' + filename);
// open and read in file contents
fs.readFile(filename, 'utf8', function(err, data) {
if (err)
res.write('Could not find or open file for reading\n');
else {
res.write(data);
}
// reponse is done
res.end();
}
setTimeout(openAndReadFile, 2000, filename, res);
In the code, the callback function on_OpenAndReadFile opens and reads a file to the HTTP response
when the function is called after approximately 2000 milliseconds have passed.
As the Node documentation carefully notes, there's no guarantee that the callback
function will be invoked in exactly n milliseconds (whatever n is). This is no different
than the use of setTimeout in a browser--we don't have absolute control over the
environment, and factors could slightly delay the timer.
The function clearTimeout clears a preset setTimeout. If you need to have a repeating timer, you
can use setInterval to call a function for every n milliseconds, n being the second parameter passed to
the function. Clear the interval with clearInterval.
Servers, Streams, and Sockets
Much of the Node core API has to do with creating services that listen to specific types of communications.
In the examples in Chapter 1, we used the HTTP module to create an HTTP web server. Other methods can
create a TCP server, a TLS (Transport Layer Security) server, and a UDP/datagram socket. I'll cover TLS
later, in Chapter 13 covering security, but in this section I want to introduce the TCP and UDP Node core
functionality. First, though, a brief introduction to the terms used in this section.
A socket is an end point in a communication, and a network socket is an end point in a communication
between applications running on two different computers running on the network. The data that flows
between the sockets is known as the stream. The data in the stream can be transmitted as binary data, in a
buffer, or in Unicode, as a string. Both types of data are transmitted as packets: parts of the data split off
into specifically sized pieces. There is a special kind of packet, a FIN, or finish packet, that is sent by a
socket to signal that it is done. How the communication is managed, and how reliable the stream, is a
consideration of the type of socket created.
TCP Sockets and Servers
We can create a basic TCP (Transmission Control Protocol) server and client with the Net module. TCP
forms the basis for most internet applications, such as web service and email. It provides a way of reliably
transmitting data between client and server sockets.
Creating the TCP server is little different than creating the HTTP server in Example 1-1 in Chapter 1. We
create the server, passing in a callback function. The TCP server differs from the HTTP server, in that
rather than passing a requestListener, the TCP callback function's sole argument is an instance of a socket,
listening for incoming connections.
Example 3-2 contains the code to create a TCP server. Once the server socket is created, it listens for two
events: when data is received, and when the client closes the connection.
Example 3-2. A simple TCP server, with a socket listening for client communication on port 8124
var net = require('net');
var server = net.createServer(function(conn) {
console.log('connected');
conn.on('data', function (data) {
console.log(data + ' from ' + conn.remoteAddress + ' ' +
conn.remotePort);
conn.write('Repeating: ' + data);
});
conn.on('close', function() {
console.log('client closed connection');
});
}).listen(8124);
console.log('listening on port 8124');
There is an optional parameter for createServer: allowHalfOpen. Setting this parameter to true
instructs the socket not to send a FIN when it receives a FIN packet from the client. Doing this keeps the
socket open for writing (not reading). To close the socket, you'd then need to explicitly use the end
method. By default, allowHalfOpen is false.
Notice how a callback function is attached to the two events via the on method. Many objects in Node that
emit events provide a way to attach a function as event listener via the use of the on method. This method
takes the name of the event as first parameter, and the function listener, as the second.
The TCP client is just as simple to create as the server, as shown in Example 3-3. The call to the
setEncoding method on the client changes the encoding for the received data. As discussed in the
section earlier in the chapter on the Buffer object, data is transmitted as a non-humanly readable buffer, but
we can use setEncoding to read it as a string. The socket's write method is used to transmit the data.
It also attaches listener functions to two events: data, for received data, and close, in case the server
closes the connection.
Example 3-3. Client Socket sending data to TCP server
var net = require('net');
var client = new net.Socket();
client.setEncoding('utf8');
// connect to server
client.connect ('8124','localhost', function () {
console.log('connected to server');
client.write('Who needs a browser to communicate?');
});
// prepare for input from terminal
process.stdin.resume();
// when receive data, send to server
process.stdin.on('data', function (data) {
client.write(data);
});
// when receive data back, print to console
client.on('data',function(data) {
console.log(data);
});
// when server closed
client.on('close',function() {
console.log('connection is closed');
});
The data being transmitted between the two sockets is typed in at the terminal, and transmitted when you
hit ENTER. The client application first sends the a string you just typed, which the TCP server writes out
to the console. The server repeats the message back to the client, which in turn writes the message out to
the console. The server also prints out the IP address and port for the client using the socket's
remoteAddress and remotePort properties. Following is the console output for the server after
several strings were sent from the client (with IP address edited out for the book):
Hey, hey, hey, hey-now.
from #ipaddress 57251
Don't be mean, we don't have to be mean.
from #ipaddress 57251
Cuz remember, no matter where you go,
from #ipaddress 57251
there you are.
from #ipaddress 57251
The connection between the client and server is maintained until one or the other is killed using CTRL-C.
Whichever socket is still open receives a close event that's printed out to the console. The server can also
serve more than one connection from more than one client, since all the relevant functions are
asynchronous.
As I mentioned earlier, TCP is the underlying

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章