LearnNode 第三章 部分翻译 附 原文

来源:互联网 发布:linux time 时间格式 编辑:程序博客网 时间:2024/05/01 12:28

第三章   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

原创粉丝点击